Skip to content

Conversation

@ekayandan
Copy link
Contributor

@ekayandan ekayandan commented Sep 16, 2025

Checklist

General

Motivation and Context

The programming exercise integration tests previously relied on broad GitService mocks in base test classes. This led to fragile behavior (e.g. NotAMockException resets, unreliable plagiarism checks, and unrealistic repository state). The PR migrates these tests to use real LocalVC-backed repositories to improve reliability, better reflect production behavior, and unblock further work on export and plagiarism robustness. A single localized SpyBean override is retained for one JPlag ZIP test to ensure parsable content without reintroducing global mocking.

Description

Removed GitService mock usage from shared/base integration test setup; now autowiring the real bean.
Refactored plagiarism preparation (prepareTwoSubmissionsForPlagiarismChecks) to seed actual LocalVC repositories with committed Java sources instead of Mockito stubbing.
Fixed teardown instability by removing invalid Mockito reset of a real bean (NotAMockException).
Added localized @SpyBean override only inside ProgrammingExerciseIntegrationJenkinsLocalVCTest to inject minimal Java code for getOrCheckoutRepositoryForJPlag, avoiding base-class pollution.
Ensured repository content is written and committed before plagiarism checks to satisfy JPlag token parsing.
Preserved existing test semantics (authorization, export flows, timeline updates) while improving determinism.
Minor cleanup and defensive exception handling around repository seeding utilities.

Review Progress

Code Review

  • Code Review 1
  • Code Review 2

Test Coverage

Summary by CodeRabbit

  • Tests

    • Refactored test infrastructure to use real LocalVC-backed repositories instead of mocks for more realistic testing scenarios.
    • Added new test utilities for repository management and cleanup.
    • Disabled several tests requiring LocalVC repository setup for investigation.
  • Chores

    • Updated test base classes to use standard dependency injection for version control services.

✏️ Tip: You can customize this high-level summary in your review settings.

…m:ls1intum/Artemis into chore/convert-athena-endpoints-to-filemap
…m:ls1intum/Artemis into chore/convert-athena-endpoints-to-filemap
…ices

- Implemented checks in `AthenaFeedbackSuggestionsService` to return an empty list if the feedback suggestion module is not configured for an exercise.
- Updated `AthenaModuleService` to throw an `IllegalArgumentException` if the exercise lacks a feedback suggestion module.
- Enhanced `AthenaRepositoryExportService` to validate repository types and throw an exception for invalid types.
- Modified `AthenaResource` to handle bad requests for invalid repository types.
- Added integration tests to ensure proper handling of invalid repository types and feedback suggestion module absence.
…m:ls1intum/Artemis into chore/convert-athena-endpoints-to-filemap
@github-project-automation github-project-automation bot moved this to Work In Progress in Artemis Development Sep 16, 2025
@github-actions github-actions bot added tests programming Pull requests that affect the corresponding module labels Sep 16, 2025
@github-actions
Copy link

End-to-End (E2E) Test Results Summary

TestsPassed ☑️Skipped ⚠️Failed ❌️Time ⏱
End-to-End (E2E) Test Report205 ran201 passed3 skipped1 failed1h 16m 40s 552ms
TestResultTime ⏱
End-to-End (E2E) Test Report
e2e/exercise/quiz-exercise/QuizExerciseManagement.spec.ts
ts.Quiz Exercise Management › Quiz Exercise Creation › Creates a Quiz with Drag and Drop❌ failure2m 5s 609ms

@github-actions github-actions bot added the server Pull requests that update Java code. (Added Automatically!) label Sep 17, 2025
@github-project-automation github-project-automation bot moved this from Work In Progress to Ready For Review in Artemis Development Nov 14, 2025
…thub.com:ls1intum/Artemis into chore/programming-exercises/improve-export-tests
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (6)
src/test/java/de/tum/cit/aet/artemis/exercise/service/ExerciseVersionServiceTest.java (1)

226-241: Critical: LocalRepository resources are not cleaned up.

The call to createAndWireBaseRepositories creates LocalRepository instances on the file system (template, solution, test repositories), but these resources are never cleaned up. This will cause disk space accumulation and test pollution across repeated test runs.

Solution: Capture the repository handles and clean them up in the @AfterEach method:

  1. Add a field to store the repository handles:
private RepositoryExportTestUtil.BaseRepositories baseRepositories;
  1. Update the repository creation to capture handles:
-            RepositoryExportTestUtil.createAndWireBaseRepositories(localVCLocalCITestService, newProgrammingExercise);
+            baseRepositories = RepositoryExportTestUtil.createAndWireBaseRepositoriesWithHandles(localVCLocalCITestService, newProgrammingExercise);
  1. Add cleanup in the tearDown method:
 @AfterEach
 void tearDown() {
+    if (baseRepositories != null) {
+        baseRepositories.templateRepository().resetLocalRepo();
+        baseRepositories.solutionRepository().resetLocalRepo();
+        baseRepositories.testsRepository().resetLocalRepo();
+    }
     exerciseVersionRepository.deleteAll();
 }

Based on PR objectives.

src/test/java/de/tum/cit/aet/artemis/exam/StudentExamIntegrationTest.java (1)

293-296: LocalRepository cleanup ineffective—studentRepos list never populated

Verification confirms the review concern is valid. The prepareStudentExamsForConduction() method receives the studentRepos list parameter but never adds any LocalRepository instances to it. The list remains empty throughout execution, making the cleanup loop at lines 293–296 a no-op. Student repositories created during ExamPrepareExercisesTestUtil.prepareExerciseStart() are not tracked, resulting in orphaned file system resources and unclosed Git handles. The prepareStudentExamsForConduction() method must be modified to add each student LocalRepository created during participation setup to the provided studentRepos list.

src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseIntegrationTestService.java (4)

485-505: Same issue: seeded repos not cleaned

This test seeds two student repos and never resets them.

Apply:

-        RepositoryExportTestUtil.seedStudentRepositoryForParticipation(localVCLocalCITestService, participation1);
-        RepositoryExportTestUtil.seedStudentRepositoryForParticipation(localVCLocalCITestService, participation2);
+        var repo1 = RepositoryExportTestUtil.seedStudentRepositoryForParticipation(localVCLocalCITestService, participation1);
+        var repo2 = RepositoryExportTestUtil.seedStudentRepositoryForParticipation(localVCLocalCITestService, participation2);
         programmingExerciseStudentParticipationRepository.saveAll(List.of(participation1, participation2));
@@
-        List<Path> entries = unzipExportedFile();
+        List<Path> entries;
+        try {
+            entries = unzipExportedFile();
+        }
+        finally {
+            RepositoryExportTestUtil.resetRepos(repo1, repo2);
+        }

507-543: Reset created base/template/student repos after assertions

templateRepository() and the student repo created via seedLocalVcBareFrom(...) are not reset.

Minimal end-of-test cleanup:

@@
-        try (Git downloadedGit = Git.open(extractedRepo1.get().toFile())) {
+        try (Git downloadedGit = Git.open(extractedRepo1.get().toFile())) {
             RevCommit commit = downloadedGit.log().setMaxCount(1).call().iterator().next();
             assertThat(commit.getAuthorIdent().getName()).isEqualTo("student");
             assertThat(commit.getFullMessage()).isEqualTo("All student changes in one commit");
         }
+        finally {
+            RepositoryExportTestUtil.resetRepos(templateRepo, studentRepo);
+        }

786-805: testsRepo working copy should be reset after generate-tests path

testsRepo created via createAndConfigureLocalRepository(...) remains on disk.

Append:

         var result = request.putWithResponseBody(path, programmingExercise, String.class, HttpStatus.OK);
         assertThat(result).startsWith("Successfully generated the structure oracle");
         request.putWithResponseBody(path, programmingExercise, String.class, HttpStatus.BAD_REQUEST);
+        RepositoryExportTestUtil.resetRepos(testsRepo);

2226-2250: setupExerciseForExport wires base repos but never cleans project

Repeated runs can leave LocalVC project folders behind, influencing later tests.

Options:

  • Prefer RepositoryExportTestUtil.createAndWireBaseRepositoriesWithHandles(...) here, return BaseRepositories to callers, and reset them after the export assertions.
  • Or, after callers finish, invoke cleanupLocalVcProjectForKey(programmingExercise.getProjectKey()) to delete the whole project folder. Choose one strategy and apply consistently across callers. Based on learnings.
♻️ Duplicate comments (7)
src/test/java/de/tum/cit/aet/artemis/exam/ExamIntegrationTest.java (1)

1922-1935: This disabled test has already been flagged.

The test remains disabled with an investigation in progress. Ensure this is tracked and resolved before merging, as previously noted.

src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseIntegrationTestService.java (5)

2092-2104: Redirect (omitBinaries): reset templateRepo at end

Template working copy isn’t cleaned.

Add:

@@
         request.getWithForwardedUrl("/api/programming/programming-exercises/" + programmingExercise.getId() + "/template-files-content" + queryParams, HttpStatus.OK,
                 "/api/programming/repository/" + savedExercise.getTemplateParticipation().getId() + "/files-content" + queryParams);
+        RepositoryExportTestUtil.resetRepos(templateRepo);

2106-2117: Redirect (files-content): reset templateRepo

Same as above.

Add:

@@
         request.getWithForwardedUrl("/api/programming/programming-exercises/" + programmingExercise.getId() + "/template-files-content", HttpStatus.OK,
                 "/api/programming/repository/" + savedExercise.getTemplateParticipation().getId() + "/files-content");
+        RepositoryExportTestUtil.resetRepos(templateRepo);

2121-2154: Participation files-at-commit: reset seeded student repo

The student repo created via seedStudentRepositoryForParticipation(...) is not cleaned.

Add:

@@
         request.getWithFileContents("/api/programming/programming-exercise-participations/" + studentParticipation.getId() + "/files-content/" + submission.getCommitHash(),
                 HttpStatus.OK, filesWithContentsAsJson);
+        RepositoryExportTestUtil.resetRepos(repo);

2156-2175: Forbidden variant: reset seeded student repo

Same leak in forbidden path.

Add:

@@
         request.get("/api/programming/programming-exercise-participations/" + participation1.getId() + "/files-content/" + submission.getCommitHash(), HttpStatus.FORBIDDEN,
                 Map.class);
+        RepositoryExportTestUtil.resetRepos(repo);

357-366: Capture seeded repos and reset them to prevent leaks

seedStudentRepositoryForParticipation(...) returns LocalRepository handles that aren’t cleaned. Capture both and reset in finally or at method end.

Suggested change:

-        RepositoryExportTestUtil.seedStudentRepositoryForParticipation(localVCLocalCITestService, participation1);
-        RepositoryExportTestUtil.seedStudentRepositoryForParticipation(localVCLocalCITestService, participation2);
+        var repo1 = RepositoryExportTestUtil.seedStudentRepositoryForParticipation(localVCLocalCITestService, participation1);
+        var repo2 = RepositoryExportTestUtil.seedStudentRepositoryForParticipation(localVCLocalCITestService, participation2);
         programmingExerciseStudentParticipationRepository.saveAll(List.of(participation1, participation2));
@@
-        return unzipExportedFile();
+        try {
+            return unzipExportedFile();
+        }
+        finally {
+            RepositoryExportTestUtil.resetRepos(repo1, repo2);
+        }
src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseGitIntegrationTest.java (1)

101-109: Remote LocalRepository must be reset in finally to prevent FS leaks

The LocalRepository created via createAndConfigureLocalRepository(...) isn’t reset. Wrap usage in try/finally and call remoteRepo.resetLocalRepo().

Suggested patch (abbrev., combine with previous comment if applied):

-        LocalRepository remoteRepo = localVCLocalCITestService.createAndConfigureLocalRepository(projectKey, repoSlug);
- 
-        // Write a file and commit on the remote working copy, then push to origin
-        var readmePath = remoteRepo.workingCopyGitRepoFile.toPath().resolve("README.md");
-        Files.writeString(readmePath, "Initial commit");
-        remoteRepo.workingCopyGitRepo.add().addFilepattern(".").call();
-        GitService.commit(remoteRepo.workingCopyGitRepo).setMessage("Initial commit").call();
-        remoteRepo.workingCopyGitRepo.push().setRemote("origin").call();
+        LocalRepository remoteRepo = localVCLocalCITestService.createAndConfigureLocalRepository(projectKey, repoSlug);
+        try {
+            // Write a file and commit on the remote working copy, then push to origin
+            var readmePath = remoteRepo.workingCopyGitRepoFile.toPath().resolve("README.md");
+            Files.writeString(readmePath, "Initial commit");
+            remoteRepo.workingCopyGitRepo.add().addFilepattern(".").call();
+            GitService.commit(remoteRepo.workingCopyGitRepo).setMessage("Initial commit").call();
+            remoteRepo.workingCopyGitRepo.push().setRemote("origin").call();
+            // ... rest of test ...
+        }
+        finally {
+            remoteRepo.resetLocalRepo();
+        }
🧹 Nitpick comments (11)
src/test/java/de/tum/cit/aet/artemis/exercise/service/ExerciseVersionServiceTest.java (1)

239-241: Consider failing the test on repository creation errors.

The exception handling has been broadened from specific Git-related exceptions to a generic Exception, and the catch block only logs the error without failing the test. If repository creation fails, the test may produce misleading results.

Consider whether the test should fail explicitly when repository setup fails:

 catch (Exception e) {
     log.error("Failed to create programming exercise", e);
+    throw new RuntimeException("Repository setup failed", e);
 }

Alternatively, if silent failure is intentional, add a comment explaining why.

src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalVCIntegrationTest.java (1)

94-102: Consider narrowing the exception type for more precise handling.

While the refactor to use RepositoryExportTestUtil.safeDeleteDirectory is good, broadening the catch from IOException to Exception may inadvertently catch runtime exceptions that indicate logic errors rather than filesystem issues.

If feasible, consider catching more specific exceptions:

 try {
     RepositoryExportTestUtil.safeDeleteDirectory(someRepository.remoteBareGitRepoFile.toPath());
 }
-catch (Exception exception) {
+catch (IOException exception) {
     // JGit creates a lock file in each repository that could cause deletion problems.
     if (exception.getMessage().contains("gc.log.lock")) {
         return;
     }
     throw exception;
 }

Note: If safeDeleteDirectory throws checked exceptions other than IOException, the current broad catch may be necessary.

src/test/java/de/tum/cit/aet/artemis/exam/StudentExamIntegrationTest.java (1)

1508-1523: Clone‑modify‑push helper is robust; small hardening optional

The try‑with‑resources Git + finally safeDeleteDirectory pattern is correct. Optional: add “git.add().addFilepattern('.')” to future‑proof nested paths; today’s single‑file add is fine.

-            git.add().addFilepattern(fileName).call();
+            git.add().addFilepattern(".").call();

Based on learnings.

src/test/java/de/tum/cit/aet/artemis/programming/RepositoryIntegrationTest.java (3)

201-217: Teardown is good; consider also deleting the LocalVC project

resetLocalRepo() + safe directory deletes are solid. To avoid residue when a test fails before @beforeeach runs (or between classes), optionally delete the LocalVC project here as well.

 @AfterEach
 void tearDown() throws IOException {
   ...
-  deleteDirectoryIfExists(Path.of("local/server-integration-test/repos"));
-  deleteDirectoryIfExists(Path.of("local/server-integration-test/repos-download"));
+  deleteDirectoryIfExists(Path.of("local/server-integration-test/repos"));
+  deleteDirectoryIfExists(Path.of("local/server-integration-test/repos-download"));
+  // Best-effort cleanup of LocalVC project artifacts created in this test
+  try {
+      deleteExistingProject(projectKey);
+  } catch (Exception ignored) {
+  }
 }

1177-1181: Seeding helper is concise; could reuse shared test util to reduce duplication

Current code is fine. If you want less boilerplate, RepositoryExportTestUtil.writeAndCommit/writeFilesAndPush offer the same add/commit/push pattern across suites.

- repository.workingCopyGitRepo.add().addFilepattern(".").call();
- GitService.commit(repository.workingCopyGitRepo).setMessage("Initial commit for " + currentLocalFileName).call();
- repository.workingCopyGitRepo.push().setRemote("origin").call();
+ RepositoryExportTestUtil.writeFilesAndPush(
+     repository,
+     Map.of(
+         currentLocalFileName, currentLocalFileContent,
+         currentLocalFolderName + "/.keep", ""
+     ),
+     "Initial commit for " + currentLocalFileName
+);

Based on learnings.

Also applies to: 1183-1196


227-231: Prefer Path.resolve over string concatenation for FS assertions

Minor portability/readability nit. Applies to similar occurrences below.

- assertThat(Path.of(studentRepository.workingCopyGitRepoFile + "/" + key)).exists();
+ assertThat(studentRepository.workingCopyGitRepoFile.toPath().resolve(key)).exists();

As per coding guidelines.

src/test/java/de/tum/cit/aet/artemis/exercise/programming/AuxiliaryRepositoryResourceIntegrationTest.java (1)

390-400: Service-unavailable path: remote deletion helper looks good

deleteRemoteAuxiliaryRepository() safely closes and removes the bare and working copies. Small nit: consider nulling the fields after close to avoid accidental reuse, but not required.

src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseParticipationIntegrationTest.java (4)

854-883: LocalVC-backed setup for template/solution/tests/aux commit history looks good

The nested GetCommitHistoryForTemplateSolutionTestOrAuxRepo setup correctly:

  • Loads an exercise with template/solution and auxiliary repos eagerly.
  • Wires LocalVC base repositories via RepositoryExportTestUtil.createAndWireBaseRepositories.
  • Seeds deterministic commits in template, solution, tests, and auxiliary repos using the new commitToRepository helper and stored commit messages.

This aligns well with the PR goal of using real LocalVC repositories instead of mocks. One minor point: userUtilService.addUsers is also called in the outer @BeforeEach; if that method is not idempotent, double invocation per nested test could cause redundant work or constraints violations. Please confirm idempotence or consider removing one of the calls.


931-941: Auxiliary repository configuration helper is correct but could guard explicitly against missing repos

ensureAuxiliaryRepositoryConfigured correctly:

  • Uses the first auxiliary repository configured on the exercise.
  • Lazily creates and configures a LocalVC repository (including URI) only if the repositoryUri is still null.
  • Persists updates via auxiliaryRepositoryRepository.save.

Given that programmingExerciseIntegrationTestService.addAuxiliaryRepositoryToExercise is called earlier, this is fine; as a defensive improvement, you could optionally assert that getAuxiliaryRepositories() is non-empty to fail fast with a clearer message if the fixture ever changes.


968-999: Commit-details view setup correctly seeds student/template/solution commits

The GetParticipationRepositoryFilesForCommitsDetailsView nested setup:

  • Creates a fresh programming exercise with eager template/solution/aux relations.
  • Wires LocalVC base repos via RepositoryExportTestUtil.createAndWireBaseRepositories.
  • Adds a student participation and uses commitToParticipationRepository to create a student commit.
  • Seeds separate commits in template and solution repos and persists all changes.
  • Builds a clear basePath for the commit-details endpoint.

This yields three independent commit hashes tied to real repositories, which is exactly what the subsequent tests rely on. Only nit: like the previous nested class, it re-calls userUtilService.addUsers; if addUsers is not idempotent with respect to the test database lifecycle, you may want to avoid duplicating that call.


1109-1159: LocalVC commit helpers are sound; consider using repositorySlug() instead of manual .git trimming

The helper chain:

  • commitToParticipationRepository delegates via getVcsRepositoryUri().
  • commitToRepository(VcsRepositoryUri) guards against null and normalizes to LocalVCRepositoryUri when necessary.
  • commitToRepository(LocalVCRepositoryUri) ensures the bare repo exists (creating it via localVCLocalCITestService.createAndConfigureLocalRepository if needed), resolves its local path, and calls writeFilesAndPush.
  • writeFilesAndPush clones the bare repo into a temp working copy under tempPath, writes all requested files, commits using GitService.commit(git).setMessage(message).call(), pushes, and then cleans up the working copy via RepositoryExportTestUtil.safeDeleteDirectory.

This achieves the PR goal of seeding real Git history while:

  • Avoiding leaks of temporary working directories.
  • Handling both LocalVCRepositoryUri and generic VcsRepositoryUri cases.

One small refactor to simplify ensureLocalVcRepositoryExists:

-    String slugWithGit = repositoryUri.getRelativeRepositoryPath().getFileName().toString();
-    String repositorySlug = slugWithGit.endsWith(".git") ? slugWithGit.substring(0, slugWithGit.length() - 4) : slugWithGit;
-    localVCLocalCITestService.createAndConfigureLocalRepository(repositoryUri.getProjectKey(), repositorySlug);
+    String repositorySlug = repositoryUri.repositorySlug();
+    localVCLocalCITestService.createAndConfigureLocalRepository(repositoryUri.getProjectKey(), repositorySlug);

Using repositorySlug() (inherited from VcsRepositoryUri) removes manual .git stripping and keeps the slug derivation consistent with the rest of the codebase. Otherwise, the logic and cleanup in these helpers look solid.

Comment on lines +110 to +114
// Build the LocalVC URI and checkout to a separate target path
LocalVCRepositoryUri repoUri = new LocalVCRepositoryUri(localVCLocalCITestService.buildLocalVCUri(null, null, projectKey, repoSlug));
Path targetPath = tempPath.resolve("lcvc-checkout").resolve("student-checkout");
var checkedOut = gitService.getOrCheckoutRepositoryWithTargetPath(repoUri, targetPath, true, true);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Also delete the checked-out local clone to avoid leftover worktrees

getOrCheckoutRepositoryWithTargetPath(...) creates a local clone under tempPath that isn’t deleted. Ensure it’s removed even on failures.

Apply:

@@
-        LocalVCRepositoryUri repoUri = new LocalVCRepositoryUri(localVCLocalCITestService.buildLocalVCUri(null, null, projectKey, repoSlug));
-        Path targetPath = tempPath.resolve("lcvc-checkout").resolve("student-checkout");
-        var checkedOut = gitService.getOrCheckoutRepositoryWithTargetPath(repoUri, targetPath, true, true);
+        LocalVCRepositoryUri repoUri = new LocalVCRepositoryUri(localVCLocalCITestService.buildLocalVCUri(null, null, projectKey, repoSlug));
+        Path targetPath = tempPath.resolve("lcvc-checkout").resolve("student-checkout");
+        de.tum.cit.aet.artemis.programming.domain.Repository checkedOut = null;
+        try {
+            checkedOut = gitService.getOrCheckoutRepositoryWithTargetPath(repoUri, targetPath, true, true);
@@
-        gitService.pullIgnoreConflicts(checkedOut);
-        gitService.resetToOriginHead(checkedOut);
+        gitService.pullIgnoreConflicts(checkedOut);
+        gitService.resetToOriginHead(checkedOut);
+        }
+        finally {
+            if (checkedOut != null) {
+                gitService.deleteLocalRepository(checkedOut);
+            }
+        }

Also applies to: 120-130

🤖 Prompt for AI Agents
In
src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseGitIntegrationTest.java
around lines 110-114 (also apply same fix to 120-130), the test uses
gitService.getOrCheckoutRepositoryWithTargetPath(...) which leaves a local clone
under tempPath; wrap the checkout and any subsequent work in a try/finally and
in the finally block reliably delete the checked-out targetPath (recursively) so
the local clone/worktree is removed even on failures; ensure any repository
handles are closed before deletion and reuse the same deletion logic for the
other occurrence at lines 120-130.

@github-actions
Copy link

End-to-End (E2E) Test Results Summary

TestsPassed ☑️Skipped ⚠️Failed ❌️Time ⏱
End-to-End (E2E) Test Report205 ran200 passed3 skipped2 failed1h 21m 51s 463ms
TestResultTime ⏱
End-to-End (E2E) Test Report
e2e/exercise/quiz-exercise/QuizExerciseDropLocation.spec.ts
ts.Quiz Exercise Drop Location Spec › DnD Quiz drop locations › Checks drop locations❌ failure2m 4s 18ms
e2e/exercise/programming/ProgrammingExerciseStaticCodeAnalysis.spec.ts
ts.Static code analysis tests › Configures SCA grading and makes a successful submission with SCA errors❌ failure2m 37s 868ms

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
src/test/java/de/tum/cit/aet/artemis/athena/AthenaResourceIntegrationTest.java (1)

403-440: Critical: Missing resource cleanup for LocalRepository.

The sourceRepo created at line 412 is never cleaned up, causing file-system resources (Git instances, directories) to accumulate across test runs. This is the exact issue raised in the PR objectives comment.

Add cleanup using RepositoryExportTestUtil.resetRepos():

         assertThat(repoFiles).as("export returns exactly one file: README.md").isNotNull().hasSize(1).containsOnlyKeys("README.md").containsEntry("README.md", "Initial commit");
+
+        // Cleanup local repository
+        RepositoryExportTestUtil.resetRepos(sourceRepo);
     }

Alternatively, wrap repository operations in try-finally or move cleanup to an @AfterEach method if multiple tests share this pattern.

src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseIntegrationTestService.java (4)

485-505: Critical: Resource leak - repositories not cleaned up.

Lines 488-489 seed LocalRepository instances without capturing them, causing filesystem resource leaks across test runs.

Apply the same cleanup pattern:

 void testExportSubmissionsByParticipationIds() throws Exception {
-    // Seed LocalVC student repositories and wire URIs
-    // use shared util to seed and wire repos for both participations
-    RepositoryExportTestUtil.seedStudentRepositoryForParticipation(localVCLocalCITestService, participation1);
-    RepositoryExportTestUtil.seedStudentRepositoryForParticipation(localVCLocalCITestService, participation2);
+    var repo1 = RepositoryExportTestUtil.seedStudentRepositoryForParticipation(localVCLocalCITestService, participation1);
+    var repo2 = RepositoryExportTestUtil.seedStudentRepositoryForParticipation(localVCLocalCITestService, participation2);
     programmingExerciseStudentParticipationRepository.saveAll(List.of(participation1, participation2));

-    var participationIds = programmingExerciseStudentParticipationRepository.findAll().stream().map(participation -> participation.getId().toString()).toList();
-    final var path = "/api/programming/programming-exercises/" + programmingExercise.getId() + "/export-repos-by-participation-ids/" + String.join(",", participationIds);
-    // all options false by default, only test if export works at all
-    var exportOptions = new RepositoryExportOptionsDTO();
-
-    downloadedFile = request.postWithResponseBodyFile(path, exportOptions, HttpStatus.OK);
-    assertThat(downloadedFile).exists();
-
-    List<Path> entries = unzipExportedFile();
-
-    // Make sure both repositories are present (by login suffix)
-    assertThat(entries).anyMatch(entry -> entry.toString().endsWith(Path.of(userPrefix + "student1", ".git").toString()))
-            .anyMatch(entry -> entry.toString().endsWith(Path.of(userPrefix + "student2", ".git").toString()));
+    try {
+        var participationIds = programmingExerciseStudentParticipationRepository.findAll().stream().map(participation -> participation.getId().toString()).toList();
+        final var path = "/api/programming/programming-exercises/" + programmingExercise.getId() + "/export-repos-by-participation-ids/" + String.join(",", participationIds);
+        var exportOptions = new RepositoryExportOptionsDTO();
+
+        downloadedFile = request.postWithResponseBodyFile(path, exportOptions, HttpStatus.OK);
+        assertThat(downloadedFile).exists();
+
+        List<Path> entries = unzipExportedFile();
+
+        assertThat(entries).anyMatch(entry -> entry.toString().endsWith(Path.of(userPrefix + "student1", ".git").toString()))
+                .anyMatch(entry -> entry.toString().endsWith(Path.of(userPrefix + "student2", ".git").toString()));
+    }
+    finally {
+        RepositoryExportTestUtil.resetRepos(repo1, repo2);
+    }
 }

Based on PR objectives comment.


507-543: Critical: Multiple repository leaks in anonymization test.

This test creates four LocalRepository instances (template, solution, tests via line 509, and student via line 518) without cleaning any of them, causing significant filesystem resource accumulation.

Capture all repositories and clean them:

 void testExportSubmissionAnonymizationCombining() throws Exception {
-    // Ensure base repos exist and retrieve handles for template/solution/tests
     var baseRepositories = RepositoryExportTestUtil.createAndWireBaseRepositoriesWithHandles(localVCLocalCITestService, programmingExercise);
     programmingExercise = programmingExerciseRepository.save(programmingExercise);

-    // Prepare template working copy handle
     var templateRepo = baseRepositories.templateRepository();
     String projectKey = programmingExercise.getProjectKey();

-    // Seed student repository from template bare and wire URI
     String studentSlug = localVCLocalCITestService.getRepositorySlug(projectKey, participation1.getParticipantIdentifier());
     var studentRepo = RepositoryExportTestUtil.seedLocalVcBareFrom(localVCLocalCITestService, projectKey, studentSlug, templateRepo);
     participation1.setRepositoryUri(localVCLocalCITestService.buildLocalVCUri(null, null, projectKey, studentSlug));
     programmingExerciseStudentParticipationRepository.save(participation1);

-    // Add a student commit to anonymize
-    localVCLocalCITestService.commitFile(studentRepo.workingCopyGitRepoFile.toPath(), studentRepo.workingCopyGitRepo, "Test.java");
-    studentRepo.workingCopyGitRepo.push().setRemote("origin").call();
-
-    // Rest call with options (combine + anonymize enabled in getOptions())
-    final var path = "/api/programming/programming-exercises/" + programmingExercise.getId() + "/export-repos-by-participation-ids/" + participation1.getId();
-    downloadedFile = request.postWithResponseBodyFile(path, getOptions(), HttpStatus.OK);
-    assertThat(downloadedFile).exists();
-
-    List<Path> entries = unzipExportedFile();
-
-    // Checks: file present and single anonymized commit
-    assertThat(entries).anyMatch(entry -> entry.endsWith("Test.java"));
-    Optional<Path> extractedRepo1 = entries.stream()
-            .filter(entry -> entry.toString().endsWith(Path.of("-" + participation1.getId() + "-student-submission.git", ".git").toString())).findFirst();
-    assertThat(extractedRepo1).isPresent();
-    try (Git downloadedGit = Git.open(extractedRepo1.get().toFile())) {
-        RevCommit commit = downloadedGit.log().setMaxCount(1).call().iterator().next();
-        assertThat(commit.getAuthorIdent().getName()).isEqualTo("student");
-        assertThat(commit.getFullMessage()).isEqualTo("All student changes in one commit");
+    try {
+        localVCLocalCITestService.commitFile(studentRepo.workingCopyGitRepoFile.toPath(), studentRepo.workingCopyGitRepo, "Test.java");
+        studentRepo.workingCopyGitRepo.push().setRemote("origin").call();
+
+        final var path = "/api/programming/programming-exercises/" + programmingExercise.getId() + "/export-repos-by-participation-ids/" + participation1.getId();
+        downloadedFile = request.postWithResponseBodyFile(path, getOptions(), HttpStatus.OK);
+        assertThat(downloadedFile).exists();
+
+        List<Path> entries = unzipExportedFile();
+
+        assertThat(entries).anyMatch(entry -> entry.endsWith("Test.java"));
+        Optional<Path> extractedRepo1 = entries.stream()
+                .filter(entry -> entry.toString().endsWith(Path.of("-" + participation1.getId() + "-student-submission.git", ".git").toString())).findFirst();
+        assertThat(extractedRepo1).isPresent();
+        try (Git downloadedGit = Git.open(extractedRepo1.get().toFile())) {
+            RevCommit commit = downloadedGit.log().setMaxCount(1).call().iterator().next();
+            assertThat(commit.getAuthorIdent().getName()).isEqualTo("student");
+            assertThat(commit.getFullMessage()).isEqualTo("All student changes in one commit");
+        }
+    }
+    finally {
+        RepositoryExportTestUtil.resetRepos(baseRepositories.templateRepository(), baseRepositories.solutionRepository(), 
+                baseRepositories.testsRepository(), studentRepo);
     }
 }

Based on PR objectives comment.


577-585: Critical: Resource leak in helper method.

Lines 579-580 create LocalRepository instances without capturing them for cleanup. This helper is called by testExportSubmissionsByStudentLogins() which also needs the cleanup.

Refactor to capture and return repositories for cleanup by the caller:

-private File exportSubmissionsByStudentLogins() throws Exception {
-    // Seed LocalVC student repositories and wire URIs
-    RepositoryExportTestUtil.seedStudentRepositoryForParticipation(localVCLocalCITestService, participation1);
-    RepositoryExportTestUtil.seedStudentRepositoryForParticipation(localVCLocalCITestService, participation2);
+private record ExportResult(File file, LocalRepository repo1, LocalRepository repo2) {}
+
+private ExportResult exportSubmissionsByStudentLogins() throws Exception {
+    var repo1 = RepositoryExportTestUtil.seedStudentRepositoryForParticipation(localVCLocalCITestService, participation1);
+    var repo2 = RepositoryExportTestUtil.seedStudentRepositoryForParticipation(localVCLocalCITestService, participation2);
     programmingExerciseStudentParticipationRepository.saveAll(List.of(participation1, participation2));
     final var path = "/api/programming/programming-exercises/" + programmingExercise.getId() + "/export-repos-by-participant-identifiers/" + userPrefix + "student1,"
             + userPrefix + "student2";
-    return request.postWithResponseBodyFile(path, getOptions(), HttpStatus.OK);
+    var file = request.postWithResponseBodyFile(path, getOptions(), HttpStatus.OK);
+    return new ExportResult(file, repo1, repo2);
 }

Then update testExportSubmissionsByStudentLogins() at lines 569-575:

 void testExportSubmissionsByStudentLogins() throws Exception {
-    downloadedFile = exportSubmissionsByStudentLogins();
-    assertThat(downloadedFile).exists();
-    List<Path> entries = unzipExportedFile();
-    // Assert both participant repos are included. For this endpoint/options, repos are anonymized and use the student-submission.git suffix
-    assertThat(entries).anyMatch(entry -> entry.toString().endsWith("student-submission.git" + File.separator + ".git"));
+    var exportResult = exportSubmissionsByStudentLogins();
+    try {
+        downloadedFile = exportResult.file();
+        assertThat(downloadedFile).exists();
+        List<Path> entries = unzipExportedFile();
+        assertThat(entries).anyMatch(entry -> entry.toString().endsWith("student-submission.git" + File.separator + ".git"));
+    }
+    finally {
+        RepositoryExportTestUtil.resetRepos(exportResult.repo1(), exportResult.repo2());
+    }
 }

Based on PR objectives comment.


786-805: Critical: Repository leaks in structure oracle test.

Lines 788 and 794 create LocalRepository instances (4 total: template, solution, tests from base, plus explicit tests repo) without cleanup.

Capture and clean up all repositories:

 void testGenerateStructureOracle() throws Exception {
-    // Wire base repositories in LocalVC and ensure tests repo has the expected directory
-    RepositoryExportTestUtil.createAndWireBaseRepositories(localVCLocalCITestService, programmingExercise);
+    var baseRepos = RepositoryExportTestUtil.createAndWireBaseRepositoriesWithHandles(localVCLocalCITestService, programmingExercise);
     programmingExercise = programmingExerciseRepository.save(programmingExercise);
     programmingExercise = programmingExerciseRepository.getProgrammingExerciseWithBuildConfigElseThrow(programmingExercise);

     String projectKey = programmingExercise.getProjectKey();
     String testsSlug = projectKey.toLowerCase() + "-" + RepositoryType.TESTS.getName();
     var testsRepo = localVCLocalCITestService.createAndConfigureLocalRepository(projectKey, testsSlug);
-    String testsPath = java.nio.file.Path.of("test", programmingExercise.getPackageFolderName()).toString();
-    if (programmingExercise.getBuildConfig().hasSequentialTestRuns()) {
-        testsPath = java.nio.file.Path.of("structural", testsPath).toString();
+    
+    try {
+        String testsPath = java.nio.file.Path.of("test", programmingExercise.getPackageFolderName()).toString();
+        if (programmingExercise.getBuildConfig().hasSequentialTestRuns()) {
+            testsPath = java.nio.file.Path.of("structural", testsPath).toString();
+        }
+        RepositoryExportTestUtil.writeFilesAndPush(testsRepo, Map.of(testsPath + "/.placeholder", ""), "Init tests dir");
+
+        final var path = "/api/programming/programming-exercises/" + programmingExercise.getId() + "/generate-tests";
+        var result = request.putWithResponseBody(path, programmingExercise, String.class, HttpStatus.OK);
+        assertThat(result).startsWith("Successfully generated the structure oracle");
+        request.putWithResponseBody(path, programmingExercise, String.class, HttpStatus.BAD_REQUEST);
+    }
+    finally {
+        RepositoryExportTestUtil.resetRepos(baseRepos.templateRepository(), baseRepos.solutionRepository(), 
+                baseRepos.testsRepository(), testsRepo);
     }
-    RepositoryExportTestUtil.writeFilesAndPush(testsRepo, Map.of(testsPath + "/.placeholder", ""), "Init tests dir");
-
-    final var path = "/api/programming/programming-exercises/" + programmingExercise.getId() + "/generate-tests";
-    var result = request.putWithResponseBody(path, programmingExercise, String.class, HttpStatus.OK);
-    assertThat(result).startsWith("Successfully generated the structure oracle");
-    request.putWithResponseBody(path, programmingExercise, String.class, HttpStatus.BAD_REQUEST);
 }

Based on PR objectives comment.

♻️ Duplicate comments (5)
src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseGitIntegrationTest.java (1)

95-131: Critical: LocalRepository and checked-out repository resources are never cleaned up.

Despite past review comments indicating this was addressed, the current code still creates multiple filesystem resources that are never released:

  1. remoteRepo (line 102): LocalRepository creates Git instances and directories but resetLocalRepo() is never called
  2. checkedOut (line 114): Repository domain object and its working directory at targetPath (line 113) are never closed or deleted
  3. Over repeated test runs, this accumulates files and open Git handles, leading to disk space exhaustion and test pollution

Wrap all resource usage in try-finally blocks and ensure cleanup:

+    @Test
+    @WithMockUser(username = TEST_PREFIX + "student1", roles = { "USER", "STUDENT" })
+    void testGitOperationsWithLocalVC() throws Exception {
+        // Create a LocalVC repository (acts as remote) and seed with an initial commit
+        var projectKey = "PROGEXGIT";
+        var repoSlug = projectKey.toLowerCase() + "-tests";
+
         LocalRepository remoteRepo = localVCLocalCITestService.createAndConfigureLocalRepository(projectKey, repoSlug);
-
-        // Write a file and commit on the remote working copy, then push to origin
-        var readmePath = remoteRepo.workingCopyGitRepoFile.toPath().resolve("README.md");
-        FileUtils.writeStringToFile(readmePath.toFile(), "Initial commit", java.nio.charset.StandardCharsets.UTF_8);
-        remoteRepo.workingCopyGitRepo.add().addFilepattern(".").call();
-        GitService.commit(remoteRepo.workingCopyGitRepo).setMessage("Initial commit").call();
-        remoteRepo.workingCopyGitRepo.push().setRemote("origin").call();
-
-        // Build the LocalVC URI and checkout to a separate target path
-        LocalVCRepositoryUri repoUri = new LocalVCRepositoryUri(localVCLocalCITestService.buildLocalVCUri(null, null, projectKey, repoSlug));
-        Path targetPath = tempPath.resolve("lcvc-checkout").resolve("student-checkout");
-        var checkedOut = gitService.getOrCheckoutRepositoryWithTargetPath(repoUri, targetPath, true, true);
-
-        // Verify we can fetch and read last commit hash from the remote
-        gitService.fetchAll(checkedOut);
-        var lastHash = gitService.getLastCommitHash(repoUri);
-        assertThat(lastHash).as("last commit hash should exist on remote").isNotNull().isInstanceOf(ObjectId.class);
-
-        // Create a local change, commit and push via GitService
-        var localFile = targetPath.resolve("hello.txt");
-        Files.createDirectories(localFile.getParent());
-        FileUtils.writeStringToFile(localFile.toFile(), "hello world", java.nio.charset.StandardCharsets.UTF_8);
-        gitService.stageAllChanges(checkedOut);
-        gitService.commitAndPush(checkedOut, "Add hello.txt", true, null);
-
-        // Pull and reset operations should not throw
-        gitService.pullIgnoreConflicts(checkedOut);
-        gitService.resetToOriginHead(checkedOut);
+        de.tum.cit.aet.artemis.programming.domain.Repository checkedOut = null;
+        Path targetPath = null;
+        try {
+            // Write a file and commit on the remote working copy, then push to origin
+            var readmePath = remoteRepo.workingCopyGitRepoFile.toPath().resolve("README.md");
+            FileUtils.writeStringToFile(readmePath.toFile(), "Initial commit", java.nio.charset.StandardCharsets.UTF_8);
+            remoteRepo.workingCopyGitRepo.add().addFilepattern(".").call();
+            GitService.commit(remoteRepo.workingCopyGitRepo).setMessage("Initial commit").call();
+            remoteRepo.workingCopyGitRepo.push().setRemote("origin").call();
+
+            // Build the LocalVC URI and checkout to a separate target path
+            LocalVCRepositoryUri repoUri = new LocalVCRepositoryUri(localVCLocalCITestService.buildLocalVCUri(null, null, projectKey, repoSlug));
+            targetPath = tempPath.resolve("lcvc-checkout").resolve("student-checkout");
+            checkedOut = gitService.getOrCheckoutRepositoryWithTargetPath(repoUri, targetPath, true, true);
+
+            // Verify we can fetch and read last commit hash from the remote
+            gitService.fetchAll(checkedOut);
+            var lastHash = gitService.getLastCommitHash(repoUri);
+            assertThat(lastHash).as("last commit hash should exist on remote").isNotNull().isInstanceOf(ObjectId.class);
+
+            // Create a local change, commit and push via GitService
+            var localFile = targetPath.resolve("hello.txt");
+            Files.createDirectories(localFile.getParent());
+            FileUtils.writeStringToFile(localFile.toFile(), "hello world", java.nio.charset.StandardCharsets.UTF_8);
+            gitService.stageAllChanges(checkedOut);
+            gitService.commitAndPush(checkedOut, "Add hello.txt", true, null);
+
+            // Pull and reset operations should not throw
+            gitService.pullIgnoreConflicts(checkedOut);
+            gitService.resetToOriginHead(checkedOut);
+        }
+        finally {
+            // Clean up checked-out repository
+            if (checkedOut != null) {
+                checkedOut.close();
+            }
+            if (targetPath != null) {
+                RepositoryExportTestUtil.safeDeleteDirectory(targetPath);
+            }
+            // Clean up LocalRepository (working copy + bare repo)
+            remoteRepo.resetLocalRepo();
+        }
+    }
src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseIntegrationTestService.java (4)

2121-2175: Critical: Repository leaks in student participation file content tests.

Both testRedirectGetParticipationRepositoryFilesWithContentAtCommit (creates 4 repos: base + student) and testRedirectGetParticipationRepositoryFilesWithContentAtCommitForbidden (creates 1 repo) leak LocalRepository instances without cleanup.

Add cleanup to both tests:

 void testRedirectGetParticipationRepositoryFilesWithContentAtCommit(String testPrefix) throws Exception {
-    // Ensure base repositories (template, solution, tests) exist and URIs are wired for this exercise
-    RepositoryExportTestUtil.createAndWireBaseRepositories(localVCLocalCITestService, programmingExercise);
+    var baseRepos = RepositoryExportTestUtil.createAndWireBaseRepositoriesWithHandles(localVCLocalCITestService, programmingExercise);
     programmingExercise = programmingExerciseRepository.save(programmingExercise);

-    // Create student participation with LocalVC repo
-    // Use a unique student to avoid repo collisions with other tests in this class
     String studentLogin = testPrefix + "student3";
     var studentParticipation = participationUtilService.addStudentParticipationForProgrammingExercise(programmingExercise, studentLogin);
     var repo = RepositoryExportTestUtil.seedStudentRepositoryForParticipation(localVCLocalCITestService, studentParticipation);
     programmingExerciseStudentParticipationRepository.save(studentParticipation);

-    // Write files in one commit and push to origin to ensure the commit exists remotely
-    var commit = RepositoryExportTestUtil.writeFilesAndPush(repo,
-            Map.of("README.md", "Initial commit", "A.java", "abc", "B.java", "cde", "C.java", "efg", "test.txt", "Initial commit"), "seed student files");
-
-    // Persist submission with commit hash
-    var submission = new ProgrammingSubmission();
-    submission.setType(SubmissionType.MANUAL);
-    submission.setCommitHash(commit.getId().getName());
-    programmingExerciseUtilService.addProgrammingSubmission(programmingExercise, submission, studentLogin);
-
-    String filesWithContentsAsJson = """
-            {
-              "test.txt" : "Initial commit",
-              "C.java" : "efg",
-              "B.java" : "cde",
-              "A.java" : "abc",
-              "README.md" : "Initial commit"
-            }""";
-
-    request.getWithFileContents("/api/programming/programming-exercise-participations/" + studentParticipation.getId() + "/files-content/" + submission.getCommitHash(),
-            HttpStatus.OK, filesWithContentsAsJson);
+    try {
+        var commit = RepositoryExportTestUtil.writeFilesAndPush(repo,
+                Map.of("README.md", "Initial commit", "A.java", "abc", "B.java", "cde", "C.java", "efg", "test.txt", "Initial commit"), "seed student files");
+
+        var submission = new ProgrammingSubmission();
+        submission.setType(SubmissionType.MANUAL);
+        submission.setCommitHash(commit.getId().getName());
+        programmingExerciseUtilService.addProgrammingSubmission(programmingExercise, submission, studentLogin);
+
+        String filesWithContentsAsJson = """
+                {
+                  "test.txt" : "Initial commit",
+                  "C.java" : "efg",
+                  "B.java" : "cde",
+                  "A.java" : "abc",
+                  "README.md" : "Initial commit"
+                }""";
+
+        request.getWithFileContents("/api/programming/programming-exercise-participations/" + studentParticipation.getId() + "/files-content/" + submission.getCommitHash(),
+                HttpStatus.OK, filesWithContentsAsJson);
+    }
+    finally {
+        RepositoryExportTestUtil.resetRepos(baseRepos.templateRepository(), baseRepos.solutionRepository(), 
+                baseRepos.testsRepository(), repo);
+    }
 }

 void testRedirectGetParticipationRepositoryFilesWithContentAtCommitForbidden(String testPrefix) throws Exception {
-    // Seed LocalVC repo for existing participation1 and create a submission for its latest commit
     var studentLogin = participation1.getParticipantIdentifier();
     var repo = RepositoryExportTestUtil.seedStudentRepositoryForParticipation(localVCLocalCITestService, participation1);
     programmingExerciseStudentParticipationRepository.save(participation1);

-    // Write files, commit, and push via util
-    var commit = RepositoryExportTestUtil.writeFilesAndPush(repo, Map.of("README.md", "Initial commit", "A.java", "abc", "B.java", "cde", "C.java", "efg"),
-            "seed student files");
-
-    // Persist submission with commit hash
-    var submission = new ProgrammingSubmission();
-    submission.setType(SubmissionType.MANUAL);
-    submission.setCommitHash(commit.getId().getName());
-    programmingExerciseUtilService.addProgrammingSubmission(programmingExercise, submission, studentLogin);
-
-    // Expect forbidden for current user
-    request.get("/api/programming/programming-exercise-participations/" + participation1.getId() + "/files-content/" + submission.getCommitHash(), HttpStatus.FORBIDDEN,
-            Map.class);
+    try {
+        var commit = RepositoryExportTestUtil.writeFilesAndPush(repo, Map.of("README.md", "Initial commit", "A.java", "abc", "B.java", "cde", "C.java", "efg"),
+                "seed student files");
+
+        var submission = new ProgrammingSubmission();
+        submission.setType(SubmissionType.MANUAL);
+        submission.setCommitHash(commit.getId().getName());
+        programmingExerciseUtilService.addProgrammingSubmission(programmingExercise, submission, studentLogin);
+
+        request.get("/api/programming/programming-exercise-participations/" + participation1.getId() + "/files-content/" + submission.getCommitHash(), HttpStatus.FORBIDDEN,
+                Map.class);
+    }
+    finally {
+        RepositoryExportTestUtil.resetRepos(repo);
+    }
 }

Based on PR objectives comment.


2092-2117: Critical: Template repository leaks in both redirect tests.

Both test_redirectGetTemplateRepositoryFilesWithContentOmitBinaries (line 2097) and test_redirectGetTemplateRepositoryFilesWithContent (line 2111) create templateRepo instances without cleanup.

Add cleanup to both tests:

 void test_redirectGetTemplateRepositoryFilesWithContentOmitBinaries() throws Exception {
-    // Wire base repos via LocalVC
     RepositoryExportTestUtil.createAndWireBaseRepositories(localVCLocalCITestService, programmingExercise);
     programmingExercise = programmingExerciseRepository.save(programmingExercise);

     var templateRepo = RepositoryExportTestUtil.createTemplateWorkingCopy(localVCLocalCITestService, programmingExercise);
-    RepositoryExportTestUtil.writeFilesAndPush(templateRepo, Map.of("A.java", "abc", "B.jar", "binaryContent"), "seed template files");
-
-    var savedExercise = programmingExerciseRepository.findByIdWithTemplateAndSolutionParticipationElseThrow(programmingExercise.getId());
-    var queryParams = "?omitBinaries=true";
-    request.getWithForwardedUrl("/api/programming/programming-exercises/" + programmingExercise.getId() + "/template-files-content" + queryParams, HttpStatus.OK,
-            "/api/programming/repository/" + savedExercise.getTemplateParticipation().getId() + "/files-content" + queryParams);
+    try {
+        RepositoryExportTestUtil.writeFilesAndPush(templateRepo, Map.of("A.java", "abc", "B.jar", "binaryContent"), "seed template files");
+
+        var savedExercise = programmingExerciseRepository.findByIdWithTemplateAndSolutionParticipationElseThrow(programmingExercise.getId());
+        var queryParams = "?omitBinaries=true";
+        request.getWithForwardedUrl("/api/programming/programming-exercises/" + programmingExercise.getId() + "/template-files-content" + queryParams, HttpStatus.OK,
+                "/api/programming/repository/" + savedExercise.getTemplateParticipation().getId() + "/files-content" + queryParams);
+    }
+    finally {
+        RepositoryExportTestUtil.resetRepos(templateRepo);
+    }
 }

 void test_redirectGetTemplateRepositoryFilesWithContent() throws Exception {
-    // Wire base repos via LocalVC
     RepositoryExportTestUtil.createAndWireBaseRepositories(localVCLocalCITestService, programmingExercise);
     programmingExercise = programmingExerciseRepository.save(programmingExercise);

     var templateRepo = RepositoryExportTestUtil.createTemplateWorkingCopy(localVCLocalCITestService, programmingExercise);
-    RepositoryExportTestUtil.writeFilesAndPush(templateRepo, Map.of("A.java", "abc", "B.java", "cde", "C.java", "efg"), "seed template files");
-
-    var savedExercise = programmingExerciseRepository.findByIdWithTemplateAndSolutionParticipationElseThrow(programmingExercise.getId());
-    request.getWithForwardedUrl("/api/programming/programming-exercises/" + programmingExercise.getId() + "/template-files-content", HttpStatus.OK,
-            "/api/programming/repository/" + savedExercise.getTemplateParticipation().getId() + "/files-content");
+    try {
+        RepositoryExportTestUtil.writeFilesAndPush(templateRepo, Map.of("A.java", "abc", "B.java", "cde", "C.java", "efg"), "seed template files");
+
+        var savedExercise = programmingExerciseRepository.findByIdWithTemplateAndSolutionParticipationElseThrow(programmingExercise.getId());
+        request.getWithForwardedUrl("/api/programming/programming-exercises/" + programmingExercise.getId() + "/template-files-content", HttpStatus.OK,
+                "/api/programming/repository/" + savedExercise.getTemplateParticipation().getId() + "/files-content");
+    }
+    finally {
+        RepositoryExportTestUtil.resetRepos(templateRepo);
+    }
 }

Based on PR objectives comment.


359-365: Critical: Resource leak - LocalRepository instances not cleaned up.

Lines 359-360 create LocalRepository instances that are never captured or cleaned up. These repositories create filesystem resources (bare repos + working copies) that accumulate across test runs, leading to disk exhaustion and test pollution.

Capture the returned repositories and ensure cleanup:

 List<Path> exportSubmissionsWithPracticeSubmissionByParticipationIds(boolean excludePracticeSubmissions) throws Exception {
-    // Seed LocalVC repositories for both participations and wire URIs
-    RepositoryExportTestUtil.seedStudentRepositoryForParticipation(localVCLocalCITestService, participation1);
-    RepositoryExportTestUtil.seedStudentRepositoryForParticipation(localVCLocalCITestService, participation2);
+    var repo1 = RepositoryExportTestUtil.seedStudentRepositoryForParticipation(localVCLocalCITestService, participation1);
+    var repo2 = RepositoryExportTestUtil.seedStudentRepositoryForParticipation(localVCLocalCITestService, participation2);

     // Set one of the participations to practice mode
     participation1.setPracticeMode(false);
     participation2.setPracticeMode(true);
     programmingExerciseStudentParticipationRepository.saveAll(List.of(participation1, participation2));

-    // Export with excludePracticeSubmissions
-    var participationIds = programmingExerciseStudentParticipationRepository.findAll().stream().map(participation -> participation.getId().toString()).toList();
-    final var path = "/api/programming/programming-exercises/" + programmingExercise.getId() + "/export-repos-by-participation-ids/" + String.join(",", participationIds);
-    var exportOptions = new RepositoryExportOptionsDTO(false, false, false, null, excludePracticeSubmissions, false, false, false, false);
-
-    downloadedFile = request.postWithResponseBodyFile(path, exportOptions, HttpStatus.OK);
-    assertThat(downloadedFile).exists();
-
-    return unzipExportedFile();
+    try {
+        var participationIds = programmingExerciseStudentParticipationRepository.findAll().stream().map(participation -> participation.getId().toString()).toList();
+        final var path = "/api/programming/programming-exercises/" + programmingExercise.getId() + "/export-repos-by-participation-ids/" + String.join(",", participationIds);
+        var exportOptions = new RepositoryExportOptionsDTO(false, false, false, null, excludePracticeSubmissions, false, false, false, false);
+
+        downloadedFile = request.postWithResponseBodyFile(path, exportOptions, HttpStatus.OK);
+        assertThat(downloadedFile).exists();
+
+        return unzipExportedFile();
+    }
+    finally {
+        RepositoryExportTestUtil.resetRepos(repo1, repo2);
+    }
 }

Apply this pattern to all methods that seed LocalRepository instances.

Based on PR objectives comment.


1738-1762: Critical: Repository leaks in plagiarism check preparation loop.

Lines 1748-1758 create up to two LocalRepository instances in a loop without capturing them for cleanup. If an exception occurs mid-loop, partial state leaks. On success, all repos leak.

Collect repositories and clean them even on exception:

 private void prepareTwoSubmissionsForPlagiarismChecks(ProgrammingExercise programmingExercise) throws IOException, GitAPIException {
     var projectKey = programmingExercise.getProjectKey();

     var exampleProgram = """
             public class Main {

                 /**
                  * DO NOT EDIT!
                  */
                 public static void main(String[] args) {
                     Main main = new Main();
                     int magicNumber = main.calculateMagicNumber();

                     System.out.println("Magic number: " + magicNumber);
                 }

                 /**
                  * Calculate the magic number.
                  *
                  * @return the magic number.
                  */
                 private int calculateMagicNumber() {
                     int a = 0;
                     int b = 5;
                     int magicNumber = 0;

                     while (a < b) {
                         magicNumber += b;
                         a++;
                     }

                     return magicNumber;
                 }
             }
             """;

-    // Get student participations (excluding instructor)
     var studentParticipations = programmingExerciseStudentParticipationRepository.findByExerciseId(programmingExercise.getId()).stream()
             .filter(p -> p.getParticipant() != null && p.getParticipant().getName() != null && !p.getParticipant().getName().contains("instructor"))
             .sorted(Comparator.comparing(DomainObject::getId)).toList();

     if (studentParticipations.size() < 2) {
         throw new IllegalStateException("Expected at least 2 student participations for plagiarism checks");
     }

-    // Seed real LocalVC repositories for the first two student participations with identical Java content
+    var seededRepos = new ArrayList<LocalRepository>();
-    for (int i = 0; i < 2 && i < studentParticipations.size(); i++) {
-        try {
-            var participation = studentParticipations.get(i);
-            var repo = RepositoryExportTestUtil.seedStudentRepositoryForParticipation(localVCLocalCITestService, participation);
-            RepositoryExportTestUtil.writeFilesAndPush(repo, Map.of("Main.java", exampleProgram), "seed plagiarism test content");
-            programmingExerciseStudentParticipationRepository.save(participation);
+    try {
+        for (int i = 0; i < 2 && i < studentParticipations.size(); i++) {
+            try {
+                var participation = studentParticipations.get(i);
+                var repo = RepositoryExportTestUtil.seedStudentRepositoryForParticipation(localVCLocalCITestService, participation);
+                seededRepos.add(repo);
+                RepositoryExportTestUtil.writeFilesAndPush(repo, Map.of("Main.java", exampleProgram), "seed plagiarism test content");
+                programmingExerciseStudentParticipationRepository.save(participation);
+            }
+            catch (Exception e) {
+                throw new RuntimeException("Failed to seed plagiarism test repository", e);
+            }
         }
-        catch (Exception e) {
-            throw new RuntimeException("Failed to seed plagiarism test repository", e);
-        }
+        Files.createDirectories(localVCBasePath.resolve(projectKey));
+    }
+    finally {
+        RepositoryExportTestUtil.resetRepos(seededRepos.toArray(LocalRepository[]::new));
     }
-
-    // Ensure the project folder exists
-    Files.createDirectories(localVCBasePath.resolve(projectKey));
 }

Note: This ensures cleanup even if the loop throws an exception after creating the first repository.

Based on PR objectives comment.

🧹 Nitpick comments (1)
src/test/java/de/tum/cit/aet/artemis/programming/RepositoryIntegrationTest.java (1)

202-217: Consider using RepositoryExportTestUtil.resetRepos for safer cleanup.

The current teardown manually calls resetLocalRepo() on each repository with null checks. If one reset throws an exception (other than IOException), subsequent repositories won't be cleaned up. The RepositoryExportTestUtil.resetRepos helper (which you've imported and use elsewhere) wraps each reset in exception handling, ensuring all repositories are cleaned regardless of individual failures.

Apply this diff to improve cleanup robustness:

 @AfterEach
 void tearDown() throws IOException {
-    if (studentRepository != null) {
-        studentRepository.resetLocalRepo();
-    }
-    if (templateRepository != null) {
-        templateRepository.resetLocalRepo();
-    }
-    if (solutionRepository != null) {
-        solutionRepository.resetLocalRepo();
-    }
-    if (testsRepository != null) {
-        testsRepository.resetLocalRepo();
-    }
+    RepositoryExportTestUtil.resetRepos(studentRepository, templateRepository, solutionRepository, testsRepository);
     deleteDirectoryIfExists(Path.of("local/server-integration-test/repos"));
     deleteDirectoryIfExists(Path.of("local/server-integration-test/repos-download"));
 }

}

@Test
void testStudentRepositoryExportEndpoint() throws Exception {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think coderabbit's suggestion makes sense here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes sense. But also throwing generic exception in tests make sense since we would prefer tests to fail early, and gather as much context possible for the failure. It is a common pattern used in our test repo.

Copy link
Contributor

@HawKhiem HawKhiem left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think most of code rabbit's suggestions make sense to me. Other than that, no other major issue to me

@github-actions
Copy link

End-to-End (E2E) Test Results Summary

TestsPassed ☑️Skipped ⚠️Failed ❌️Time ⏱
End-to-End (E2E) Test Report205 ran200 passed3 skipped2 failed1h 17m 24s 332ms
TestResultTime ⏱
End-to-End (E2E) Test Report
e2e/exercise/quiz-exercise/QuizExerciseDropLocation.spec.ts
ts.Quiz Exercise Drop Location Spec › DnD Quiz drop locations › Checks drop locations❌ failure2m 3s 771ms
e2e/exam/test-exam/TestExamParticipation.spec.ts
ts.Test exam participation › Early Hand-in › Using exercise overview to navigate within exam❌ failure3m 48s 645ms

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java (1)

361-393: Critical: tearDown does not clean up tracked LocalRepository instances.

The repositoryMetadata.clear() at line 392 removes map entries without calling resetLocalRepo() on the tracked repositories. Even repositories properly registered via configureLocalRepositoryForSlug (line 437) will leak file-system resources and open Git handles.

Impact: Disk space accumulation, open file handles, and test pollution across repeated runs.

Apply this diff to properly clean up tracked repositories before clearing the map:

 public void tearDown() throws Exception {
     if (exerciseRepo != null) {
         exerciseRepo.resetLocalRepo();
     }
     if (testRepo != null) {
         testRepo.resetLocalRepo();
     }
     if (solutionRepo != null) {
         solutionRepo.resetLocalRepo();
     }
     if (auxRepo != null) {
         auxRepo.resetLocalRepo();
     }
     if (sourceExerciseRepo != null) {
         sourceExerciseRepo.resetLocalRepo();
     }
     if (sourceTestRepo != null) {
         sourceTestRepo.resetLocalRepo();
     }
     if (sourceSolutionRepo != null) {
         sourceSolutionRepo.resetLocalRepo();
     }
     if (sourceAuxRepo != null) {
         sourceAuxRepo.resetLocalRepo();
     }
     if (studentRepo != null) {
         studentRepo.resetLocalRepo();
     }
     if (studentTeamRepo != null) {
         studentTeamRepo.resetLocalRepo();
     }
+    // Clean up all repositories tracked in metadata
+    for (LocalRepository repo : new ArrayList<>(repositoryMetadata.keySet())) {
+        try {
+            repo.resetLocalRepo();
+        }
+        catch (IOException ignored) {
+            // Log and continue - cleanup failures shouldn't break tearDown
+        }
+    }
     repositoryMetadata.clear();
 }
♻️ Duplicate comments (2)
src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java (1)

575-615: Critical: LocalRepository resource leak in setupExerciseForExport (duplicate concern).

The LocalRepository instances created at lines 610-612 are not tracked in repositoryMetadata, so they will never be cleaned up even after the tearDown fix above. This is the same issue identified in the past review.

Impact: Every call to setupExerciseForExport leaks three LocalRepository instances (template, solution, tests), accumulating disk space and leaving Git resources open.

Apply this diff to track the created repositories in metadata:

     String projectKey = programmingExercise.getProjectKey();
     String templateRepositorySlug = projectKey.toLowerCase() + "-exercise";
     String solutionRepositorySlug = projectKey.toLowerCase() + "-solution";
     String testsRepositorySlug = projectKey.toLowerCase() + "-tests";
 
     var templateParticipation = programmingExercise.getTemplateParticipation();
     templateParticipation.setRepositoryUri(localVCLocalCITestService.buildLocalVCUri(null, null, projectKey, templateRepositorySlug));
     templateProgrammingExerciseParticipationRepository.save(templateParticipation);
 
     var solutionParticipation = programmingExercise.getSolutionParticipation();
     solutionParticipation.setRepositoryUri(localVCLocalCITestService.buildLocalVCUri(null, null, projectKey, solutionRepositorySlug));
     solutionProgrammingExerciseParticipationRepository.save(solutionParticipation);
 
     programmingExercise.setTestRepositoryUri(localVCLocalCITestService.buildLocalVCUri(null, null, projectKey, testsRepositorySlug));
 
-    localVCLocalCITestService.createAndConfigureLocalRepository(projectKey, templateRepositorySlug);
-    localVCLocalCITestService.createAndConfigureLocalRepository(projectKey, solutionRepositorySlug);
-    localVCLocalCITestService.createAndConfigureLocalRepository(projectKey, testsRepositorySlug);
+    // Track repositories in metadata so tearDown can clean them up
+    LocalRepository templateRepo = localVCLocalCITestService.createAndConfigureLocalRepository(projectKey, templateRepositorySlug);
+    LocalRepository solutionRepo = localVCLocalCITestService.createAndConfigureLocalRepository(projectKey, solutionRepositorySlug);
+    LocalRepository testsRepo = localVCLocalCITestService.createAndConfigureLocalRepository(projectKey, testsRepositorySlug);
+    
+    repositoryMetadata.put(templateRepo, new RepositoryMetadata(projectKey, templateRepositorySlug));
+    repositoryMetadata.put(solutionRepo, new RepositoryMetadata(projectKey, solutionRepositorySlug));
+    repositoryMetadata.put(testsRepo, new RepositoryMetadata(projectKey, testsRepositorySlug));
 
     return programmingExerciseRepository.save(programmingExercise);
 }

This fix works together with the tearDown fix above to ensure all dynamically created LocalRepository instances are properly cleaned up.

src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingSubmissionIntegrationTest.java (1)

74-75: Track LocalRepository instances and reset them in @AfterEach to avoid leaking Git handles and temp repos.

seedRepositoryForParticipation(...) creates LocalRepository instances that allocate working copies/bare repos on disk, but these handles are not tracked or cleaned up in tearDown(). Similarly, the base repositories created in init() via RepositoryExportTestUtil.createAndWireBaseRepositories(...) are never reset. Over repeated runs this can accumulate file-system state and open Git resources, leading to flaky tests and disk bloat.

You already have RepositoryExportTestUtil.resetRepos(...) for cleanup; consider tracking all created repos and resetting them in @AfterEach. For example:

 class ProgrammingSubmissionIntegrationTest extends AbstractProgrammingIntegrationJenkinsLocalVCTest {

@@
-    private final Map<String, String> participationCommitHashes = new HashMap<>();
+    private final Map<String, String> participationCommitHashes = new HashMap<>();
+    private final List<LocalRepository> createdRepos = new ArrayList<>();
@@
     @BeforeEach
-    void init() throws Exception {
+    void init() throws Exception {
@@
-        exercise = ExerciseUtilService.getFirstExerciseWithType(course, ProgrammingExercise.class);
-        RepositoryExportTestUtil.createAndWireBaseRepositories(localVCLocalCITestService, exercise);
+        exercise = ExerciseUtilService.getFirstExerciseWithType(course, ProgrammingExercise.class);
+        RepositoryExportTestUtil.BaseRepositories baseRepos =
+                RepositoryExportTestUtil.createAndWireBaseRepositoriesWithHandles(localVCLocalCITestService, exercise);
+        createdRepos.add(baseRepos.templateRepository());
+        createdRepos.add(baseRepos.solutionRepository());
+        createdRepos.add(baseRepos.testsRepository());
@@
     @AfterEach
     void tearDown() throws Exception {
+        RepositoryExportTestUtil.resetRepos(createdRepos.toArray(LocalRepository[]::new));
+        createdRepos.clear();
+        participationCommitHashes.clear();
         jenkinsRequestMockProvider.reset();
     }
@@
-    private void seedRepositoryForParticipation(ProgrammingExerciseStudentParticipation participation, String filename) throws Exception {
-        LocalRepository repo = RepositoryExportTestUtil.seedStudentRepositoryForParticipation(localVCLocalCITestService, participation);
+    private void seedRepositoryForParticipation(ProgrammingExerciseStudentParticipation participation, String filename) throws Exception {
+        LocalRepository repo = RepositoryExportTestUtil.seedStudentRepositoryForParticipation(localVCLocalCITestService, participation);
+        createdRepos.add(repo);
         RepositoryExportTestUtil.writeFilesAndPush(repo, Map.of(filename, "class %s {}".formatted(filename.replace('.', '_'))), "seed " + filename);
         participationRepository.save(participation);
         var latestCommit = RepositoryExportTestUtil.getLatestCommit(repo);
         participationCommitHashes.put(participation.getParticipantIdentifier(), latestCommit.getName());
     }

This keeps LocalVC-backed repos deterministic for the tests while ensuring they are fully torn down after each test execution.

Also applies to: 76-83, 100-103, 772-778

🧹 Nitpick comments (3)
src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseLocalVCJenkinsIntegrationTest.java (3)

12-18: GitService spy wiring looks good, but consider resetting stubs between tests

Using @MockitoSpyBean GitService gitServiceSpy plus the extra Mockito imports is a good fit for simulating rare Git failure scenarios on top of the real LocalVC-backed bean. One thing to double‑check: stubbings like doThrow(...) / doReturn(...) on this spy will survive beyond the individual test method unless they are reset somewhere (base classes or this class).

If there is no global reset for gitServiceSpy in a shared base test, consider adding a reset in this class’ @AfterEach to keep tests isolated, e.g.:

@AfterEach
void tearDown() throws Exception {
    programmingExerciseTestService.tearDown();
    jenkinsRequestMockProvider.reset();
    aeolusRequestMockProvider.reset();
    org.mockito.Mockito.reset(gitServiceSpy);
}

This avoids later tests unexpectedly seeing commitAndPush / copyBareRepositoryWithoutHistory / getDefaultLocalPathOfRepo still stubbed from a previous scenario.

Also applies to: 43-45, 61-63


282-289: Combined GitException + ZIP failure stubbing in importExerciseFromFile_exception_directoryDeleted

The additional doThrow(new GitException()) on gitServiceSpy.commitAndPush(...) complements the existing ZIP extraction failure and should still drive the test towards the “directory gets scheduled for deletion” path.

If the intent is to separately cover “Git commit fails” vs “ZIP extraction fails”, you might consider splitting this into two focused tests (one stubbing only commitAndPush, one only extractZipFileRecursively) to make the failure mode being exercised explicit. As it stands, it’s correct but a bit harder to tell which failure is actually under test.


483-488: configureRepository_testBadRequestError: targeted GitService failure stubbing

The new doThrow(new IOException()) on gitServiceSpy.copyBareRepositoryWithoutHistory(...) is a straightforward way to hit the “Bad Request due to copy failure” path and aligns with the LocalVC‑backed setup.

The only caveat is the same as above: ensure the stub is reset after the test (either via a shared @AfterEach or local reset), otherwise later tests might still observe copyBareRepositoryWithoutHistory throwing.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1cc5e86 and 40ce916.

📒 Files selected for processing (4)
  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseLocalVCJenkinsIntegrationTest.java (6 hunks)
  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseParticipationIntegrationTest.java (14 hunks)
  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingSubmissionIntegrationTest.java (11 hunks)
  • src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java (30 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
src/test/java/**/*.java

⚙️ CodeRabbit configuration file

test_naming: descriptive; test_size: small_specific; fixed_data: true; junit5_features: true; assert_use: assertThat; assert_specificity: true; archunit_use: enforce_package_rules; db_query_count_tests: track_performance; util_service_factory_pattern: true; avoid_db_access: true; mock_strategy: static_mocks; context_restart_minimize: true

Files:

  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingSubmissionIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseParticipationIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseLocalVCJenkinsIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java
🧠 Learnings (45)
📓 Common learnings
Learnt from: magaupp
Repo: ls1intum/Artemis PR: 8802
File: src/main/resources/templates/rust/exercise/src/context.rs:1-1
Timestamp: 2024-10-08T15:35:42.972Z
Learning: Code inside the `exercise` directories in the Artemis platform is provided to students and is intended for them to implement. TODO comments in these directories are meant to guide students and should not be addressed in the PR.
Learnt from: magaupp
Repo: ls1intum/Artemis PR: 8802
File: src/main/resources/templates/rust/exercise/src/context.rs:1-1
Timestamp: 2024-08-05T00:11:50.650Z
Learning: Code inside the `exercise` directories in the Artemis platform is provided to students and is intended for them to implement. TODO comments in these directories are meant to guide students and should not be addressed in the PR.
Learnt from: ekayandan
Repo: ls1intum/Artemis PR: 11027
File: src/main/java/de/tum/cit/aet/artemis/programming/service/GitService.java:1455-1465
Timestamp: 2025-08-11T12:57:51.535Z
Learning: In the Artemis project, when ekayandan responds with "same as above" to a code review suggestion, it means they want to defer the suggested change to a follow-up PR to keep the current PR scope minimal and focused on the core functionality being implemented.
Learnt from: SamuelRoettgermann
Repo: ls1intum/Artemis PR: 9303
File: src/main/java/de/tum/in/www1/artemis/service/exam/StudentExamService.java:296-300
Timestamp: 2024-10-20T18:37:45.365Z
Learning: When reviewing code changes in `StudentExamService.saveSubmission`, if the PR aims to improve readability without changing logic, avoid suggesting changes that alter logic, such as adding exceptions in the default case of switch statements.
Learnt from: Wallenstein61
Repo: ls1intum/Artemis PR: 10989
File: src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportFromFileService.java:108-117
Timestamp: 2025-06-10T12:26:42.449Z
Learning: In ProgrammingExerciseImportFromFileService, the current directory traversal logic for sharing imports (walking importExerciseDir before extraction) is working correctly in practice and should not be changed to more complex solutions, even if there are theoretical issues with the approach.
📚 Learning: 2024-11-26T20:43:17.588Z
Learnt from: magaupp
Repo: ls1intum/Artemis PR: 9751
File: src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseFactory.java:143-148
Timestamp: 2024-11-26T20:43:17.588Z
Learning: In `src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseFactory.java`, the test package name assigned in `populateUnreleasedProgrammingExercise` does not need to adhere to naming conventions.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingSubmissionIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseParticipationIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseLocalVCJenkinsIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java
📚 Learning: 2025-07-07T11:43:11.736Z
Learnt from: bassner
Repo: ls1intum/Artemis PR: 10705
File: src/test/java/de/tum/cit/aet/artemis/lecture/service/SlideUnhideServiceTest.java:54-57
Timestamp: 2025-07-07T11:43:11.736Z
Learning: In the Artemis test framework, the AbstractArtemisIntegrationTest base class provides common MockitoSpyBean fields like instanceMessageSendService as protected fields, making them available to all test subclasses through inheritance.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingSubmissionIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseParticipationIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseLocalVCJenkinsIntegrationTest.java
📚 Learning: 2025-08-10T18:33:22.476Z
Learnt from: Wallenstein61
Repo: ls1intum/Artemis PR: 10989
File: src/test/java/de/tum/cit/aet/artemis/programming/AbstractProgrammingIntegrationLocalCILocalVCTest.java:3-9
Timestamp: 2025-08-10T18:33:22.476Z
Learning: In the Artemis test framework, `MockitoBean` from `org.springframework.test.context.bean.override.mockito` is the standard annotation used for mocking beans in test classes, not `MockBean`. This is used consistently across test base classes like `AbstractProgrammingIntegrationLocalCILocalVCTest`, `AbstractSpringIntegrationIndependentTest`, and `AbstractSpringIntegrationLocalVCSamlTest`. The project also uses `MockitoSpyBean` from the same package.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingSubmissionIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseParticipationIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseLocalVCJenkinsIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java
📚 Learning: 2024-06-10T19:44:09.116Z
Learnt from: julian-christl
Repo: ls1intum/Artemis PR: 8052
File: src/test/java/de/tum/in/www1/artemis/lecture/CompetencyIntegrationTest.java:310-310
Timestamp: 2024-06-10T19:44:09.116Z
Learning: Modifications to parameters in `competencyProgressUtilService.createCompetencyProgress` for debugging purposes are considered irrelevant to the test outcomes but helpful for clarity during debugging.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingSubmissionIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java
📚 Learning: 2025-08-08T08:50:28.791Z
Learnt from: tobias-lippert
Repo: ls1intum/Artemis PR: 11248
File: src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseParticipationService.java:401-402
Timestamp: 2025-08-08T08:50:28.791Z
Learning: In src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseParticipationService.java, method findStudentParticipationWithLatestSubmissionResultAndFeedbacksElseThrow(long), using List.of() for latestSubmission.setResults(...) is acceptable because the results list is not mutated afterward and is only returned to the client; no follow-up code appends to it.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingSubmissionIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseParticipationIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java
📚 Learning: 2024-10-08T15:35:42.972Z
Learnt from: Strohgelaender
Repo: ls1intum/Artemis PR: 8574
File: src/main/java/de/tum/in/www1/artemis/service/tutorialgroups/TutorialGroupService.java:0-0
Timestamp: 2024-10-08T15:35:42.972Z
Learning: The `tryToFindMatchingUsers` method in `TutorialGroupService.java` has been updated to skip registrations without a student, enhancing the method's robustness. This change was implemented in commit `bef30f9751de0913143e8cb28cc0088264052261`.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingSubmissionIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseParticipationIntegrationTest.java
📚 Learning: 2025-08-27T09:46:36.480Z
Learnt from: ekayandan
Repo: ls1intum/Artemis PR: 11027
File: src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java:1839-1839
Timestamp: 2025-08-27T09:46:36.480Z
Learning: In the Artemis codebase, when stubbing GitService.getBareRepository() in tests, it's valid to return a JGit Repository (org.eclipse.jgit.lib.Repository) even though the method signature returns the domain Repository (de.tum.cit.aet.artemis.programming.domain.Repository), because the domain Repository extends JGit's FileRepository, making them polymorphically compatible.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingSubmissionIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseParticipationIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseLocalVCJenkinsIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java
📚 Learning: 2024-10-08T15:35:48.767Z
Learnt from: jakubriegel
Repo: ls1intum/Artemis PR: 8050
File: src/test/java/de/tum/in/www1/artemis/plagiarism/PlagiarismUtilService.java:125-136
Timestamp: 2024-10-08T15:35:48.767Z
Learning: The `createTeamTextExerciseAndSimilarSubmissions` method in `PlagiarismUtilService.java` uses fixed inputs as it is designed to be a test helper method for simplifying the setup of team exercises and submissions in tests.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingSubmissionIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseLocalVCJenkinsIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java
📚 Learning: 2025-06-15T04:13:22.541Z
Learnt from: SamuelRoettgermann
Repo: ls1intum/Artemis PR: 10921
File: src/test/java/de/tum/cit/aet/artemis/exam/ExamIntegrationTest.java:1334-1339
Timestamp: 2025-06-15T04:13:22.541Z
Learning: In Artemis ExamIntegrationTest, time difference assertions use ChronoUnit.MILLIS.between().isLessThan(1) without Math.abs() because the server only stores and retrieves timestamp values without calling now(), making differences predictable and consistent due to serialization/storage precision rather than timing variations.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingSubmissionIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseParticipationIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseLocalVCJenkinsIntegrationTest.java
📚 Learning: 2025-08-11T13:22:05.140Z
Learnt from: Wallenstein61
Repo: ls1intum/Artemis PR: 10989
File: src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceImportTest.java:128-131
Timestamp: 2025-08-11T13:22:05.140Z
Learning: In the ExerciseSharingResourceImportTest class in Artemis, mockServer.verify() is not needed in the tearDown method as the test design doesn't require strict verification of all mocked HTTP expectations. The mockServer field is managed differently in this test class.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingSubmissionIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseParticipationIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseLocalVCJenkinsIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java
📚 Learning: 2024-10-08T15:35:42.972Z
Learnt from: jakubriegel
Repo: ls1intum/Artemis PR: 8050
File: src/test/java/de/tum/in/www1/artemis/plagiarism/PlagiarismUtilService.java:62-66
Timestamp: 2024-10-08T15:35:42.972Z
Learning: The `createCourseWithUsers` method in `PlagiarismUtilService.java` uses fixed inputs as it is designed to be a test helper method for simplifying the setup of courses and users in tests.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingSubmissionIntegrationTest.java
📚 Learning: 2025-09-20T16:47:54.380Z
Learnt from: MoritzSpengler
Repo: ls1intum/Artemis PR: 11382
File: src/main/java/de/tum/cit/aet/artemis/quiz/service/QuizTrainingService.java:43-54
Timestamp: 2025-09-20T16:47:54.380Z
Learning: In QuizTrainingService.submitForTraining, cross-course mismatch protection is handled through PreAuthorize("hasAccessToCourse(#courseId)") authorization at the REST layer, ensuring users can only submit for courses they have access to, rather than through explicit courseId validation in the service method.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingSubmissionIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java
📚 Learning: 2025-09-25T20:28:36.905Z
Learnt from: SamuelRoettgermann
Repo: ls1intum/Artemis PR: 11419
File: src/main/java/de/tum/cit/aet/artemis/exam/domain/ExamUser.java:16-17
Timestamp: 2025-09-25T20:28:36.905Z
Learning: In the Artemis codebase, ExamUser entity uses ExamSeatDTO as a transient field for performance reasons. SamuelRoettgermann tested domain value objects but they caused 60x slower performance. This architectural exception is approved by maintainers due to significant performance benefits and Artemis naming convention requirements.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingSubmissionIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java
📚 Learning: 2024-06-10T19:44:09.116Z
Learnt from: valentin-boehm
Repo: ls1intum/Artemis PR: 7384
File: src/test/java/de/tum/in/www1/artemis/exam/StudentExamIntegrationTest.java:988-993
Timestamp: 2024-06-10T19:44:09.116Z
Learning: The `testSubmitStudentExam_differentUser` method does not require additional checks to verify the state of `studentExam1` after receiving a `HttpStatus.FORBIDDEN` because the control flow in the `StudentExamResource` is straightforward and ensures no state change occurs.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingSubmissionIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseParticipationIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseLocalVCJenkinsIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java
📚 Learning: 2025-09-20T16:47:54.380Z
Learnt from: MoritzSpengler
Repo: ls1intum/Artemis PR: 11382
File: src/main/java/de/tum/cit/aet/artemis/quiz/service/QuizTrainingService.java:43-54
Timestamp: 2025-09-20T16:47:54.380Z
Learning: In QuizTrainingService.submitForTraining, cross-course validation is handled by the REST layer through authCheckService.checkHasAtLeastRoleInCourseElseThrow() which validates user access to the course before the service method is called, eliminating the need for additional courseId validation in the service layer.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingSubmissionIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java
📚 Learning: 2025-08-26T13:23:05.331Z
Learnt from: tobias-lippert
Repo: ls1intum/Artemis PR: 11318
File: src/main/java/de/tum/cit/aet/artemis/exercise/web/ParticipationResource.java:549-552
Timestamp: 2025-08-26T13:23:05.331Z
Learning: The method findGradeScoresForAllExercisesForCourseAndStudent in StudentParticipationRepository handles both individual and team exercises by combining results from separate queries for individual grades, individual quiz grades, and team grades.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingSubmissionIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseParticipationIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java
📚 Learning: 2025-06-17T12:31:09.178Z
Learnt from: jfr2102
Repo: ls1intum/Artemis PR: 10983
File: src/main/java/de/tum/cit/aet/artemis/exercise/repository/StudentParticipationRepository.java:110-126
Timestamp: 2025-06-17T12:31:09.178Z
Learning: The query `findByExamIdWithEagerLatestLegalSubmissionsRatedResultAndIgnoreTestRunParticipation` in StudentParticipationRepository fetches all rated results (not just the latest) because the second correction round feature requires access to multiple assessment results per submission for proper correction round management.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingSubmissionIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseParticipationIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java
📚 Learning: 2025-08-19T09:13:51.235Z
Learnt from: ekayandan
Repo: ls1intum/Artemis PR: 11027
File: src/main/java/de/tum/cit/aet/artemis/programming/service/GitArchiveHelper.java:0-0
Timestamp: 2025-08-19T09:13:51.235Z
Learning: The domain Repository class (de.tum.cit.aet.artemis.programming.domain.Repository) extends JGit's FileRepository, making it polymorphically compatible with JGit APIs while providing additional Artemis-specific methods like getParticipation() and getLocalPath().

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingSubmissionIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseParticipationIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java
📚 Learning: 2025-02-11T12:05:49.151Z
Learnt from: janthoXO
Repo: ls1intum/Artemis PR: 9406
File: src/main/java/de/tum/cit/aet/artemis/programming/web/ProgrammingExerciseParticipationResource.java:209-209
Timestamp: 2025-02-11T12:05:49.151Z
Learning: In ProgrammingExerciseParticipationResource, exam-related authorization checks and sensitive information filtering for results and feedbacks are handled by resultService.filterSensitiveInformationIfNecessary().

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingSubmissionIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseParticipationIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java
📚 Learning: 2025-08-26T14:24:26.644Z
Learnt from: tobias-lippert
Repo: ls1intum/Artemis PR: 11334
File: src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseStudentParticipationRepository.java:103-114
Timestamp: 2025-08-26T14:24:26.644Z
Learning: The Artemis codebase has an existing index `idx_submission_participation_submission_date` on the `submission` table with columns `participation_id` and `submission_date DESC`. This index provides performance benefits for queries that filter submissions by participation and need ordering by submission date. The `submission` table stores all submission types including ProgrammingSubmission via single-table inheritance with discriminator values.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingSubmissionIntegrationTest.java
📚 Learning: 2024-10-08T15:35:42.972Z
Learnt from: magaupp
Repo: ls1intum/Artemis PR: 8802
File: src/main/resources/templates/rust/exercise/src/context.rs:1-1
Timestamp: 2024-10-08T15:35:42.972Z
Learning: Code inside the `exercise` directories in the Artemis platform is provided to students and is intended for them to implement. TODO comments in these directories are meant to guide students and should not be addressed in the PR.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseParticipationIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java
📚 Learning: 2025-10-22T21:31:54.240Z
Learnt from: Elfari1028
Repo: ls1intum/Artemis PR: 11491
File: src/main/java/de/tum/cit/aet/artemis/exercise/web/ExerciseResource.java:376-378
Timestamp: 2025-10-22T21:31:54.240Z
Learning: In Artemis, ExerciseVersionService.createExerciseVersion(...) (src/main/java/de/tum/cit/aet/artemis/exercise/service/ExerciseVersionService.java) eagerly re-fetches the target exercise (via type-specific findForVersioningById) before building the ExerciseSnapshotDTO. Controllers (e.g., ExerciseResource.toggleSecondCorrectionEnabled) do not need to reload the exercise before invoking createExerciseVersion.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseParticipationIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseLocalVCJenkinsIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java
📚 Learning: 2025-09-15T11:18:26.439Z
Learnt from: SamuelRoettgermann
Repo: ls1intum/Artemis PR: 11378
File: src/main/java/de/tum/cit/aet/artemis/exam/service/ExamRoomDistributionService.java:13-16
Timestamp: 2025-09-15T11:18:26.439Z
Learning: In ExamRoomDistributionService.distributeRegisteredStudents method in src/main/java/de/tum/cit/aet/artemis/exam/service/ExamRoomDistributionService.java, the team has decided not to use Transactional annotation despite multiple repository operations, based on human reviewer consultation.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseParticipationIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java
📚 Learning: 2025-02-06T17:28:16.450Z
Learnt from: magaupp
Repo: ls1intum/Artemis PR: 10265
File: src/main/resources/templates/dart/test/test/behavior_test.dart:57-67
Timestamp: 2025-02-06T17:28:16.450Z
Learning: In Artemis test files, reflection is intentionally used to bypass potential implementation errors in constructors and setters, allowing tests to produce meaningful results about core functionality even when students haven't implemented everything correctly.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseParticipationIntegrationTest.java
📚 Learning: 2024-07-17T12:13:45.428Z
Learnt from: SimonEntholzer
Repo: ls1intum/Artemis PR: 8929
File: src/main/java/de/tum/in/www1/artemis/repository/ParticipationVCSAccessTokenRepository.java:27-29
Timestamp: 2024-07-17T12:13:45.428Z
Learning: The method naming convention `deleteByParticipation_id` in `ParticipationVCSAccessTokenRepository.java` is necessary due to specific framework or library constraints in the Artemis project.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseParticipationIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java
📚 Learning: 2025-02-06T17:24:13.928Z
Learnt from: magaupp
Repo: ls1intum/Artemis PR: 10265
File: src/main/resources/templates/dart/exercise/lib/bubble_sort.dart:1-1
Timestamp: 2025-02-06T17:24:13.928Z
Learning: In programming exercise template files (e.g., src/main/resources/templates/dart/exercise/*), implementations should be left empty or incomplete as they are meant to be completed by students as part of the exercise.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseParticipationIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java
📚 Learning: 2025-05-11T22:58:13.489Z
Learnt from: theblobinthesky
Repo: ls1intum/Artemis PR: 10581
File: src/main/webapp/app/exercise/import/from-file/exercise-import-from-file.component.ts:57-60
Timestamp: 2025-05-11T22:58:13.489Z
Learning: The ProgrammingExerciseBuildConfig constructor initializes all fields with default values, including new fields like allowBranching (false) and branchRegex ('.*'), so explicit handling of these fields isn't needed when importing old exercise configurations.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseParticipationIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java
📚 Learning: 2025-08-30T20:20:17.236Z
Learnt from: SamuelRoettgermann
Repo: ls1intum/Artemis PR: 11111
File: src/test/java/de/tum/cit/aet/artemis/exam/ExamRoomIntegrationTest.java:0-0
Timestamp: 2025-08-30T20:20:17.236Z
Learning: In ExamRoomIntegrationTest.validateAdminOverview(), the first assertion should use subset containment (contains) rather than exact equality because the admin overview shows all newest unique exam rooms in the system including those from previous uploads, not just rooms from the current upload being tested. The test only needs to verify that the expected rooms from the current upload are present in the response.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseParticipationIntegrationTest.java
📚 Learning: 2024-06-10T19:44:09.116Z
Learnt from: valentin-boehm
Repo: ls1intum/Artemis PR: 7384
File: src/test/java/de/tum/in/www1/artemis/exam/StudentExamIntegrationTest.java:2836-2846
Timestamp: 2024-06-10T19:44:09.116Z
Learning: The `testAbandonStudentExamNotInTime` method does not require additional checks to verify the state of `studentExam1` after receiving a `HttpStatus.FORBIDDEN` because the control flow in the `StudentExamResource` is straightforward and ensures no state change occurs.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseParticipationIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java
📚 Learning: 2024-06-10T19:44:09.116Z
Learnt from: valentin-boehm
Repo: ls1intum/Artemis PR: 7384
File: src/test/java/de/tum/in/www1/artemis/exam/StudentExamIntegrationTest.java:975-980
Timestamp: 2024-06-10T19:44:09.116Z
Learning: The `testSubmitStudentExam_notInTime` method does not require additional checks to verify the state of `studentExam1` after receiving a `HttpStatus.FORBIDDEN` because the control flow in the `StudentExamResource` is straightforward and ensures no state change occurs.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseParticipationIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java
📚 Learning: 2024-10-27T11:50:42.955Z
Learnt from: bensofficial
Repo: ls1intum/Artemis PR: 9608
File: src/main/java/de/tum/cit/aet/artemis/programming/service/gitlabci/GitLabCIService.java:202-203
Timestamp: 2024-10-27T11:50:42.955Z
Learning: In `src/main/java/de/tum/cit/aet/artemis/programming/service/gitlabci/GitLabCIService.java`, exception messages are used internally, and minor inconsistencies are acceptable due to performance considerations.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseLocalVCJenkinsIntegrationTest.java
📚 Learning: 2025-09-01T13:47:02.624Z
Learnt from: Michael-Breu-UIbk
Repo: ls1intum/Artemis PR: 10989
File: src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.with-sharing.spec.ts:167-169
Timestamp: 2025-09-01T13:47:02.624Z
Learning: In Jest tests, prefer using jest.spyOn() over direct method assignment for mocking service methods, as spies are automatically restored by Jest's cleanup mechanisms (jest.restoreAllMocks()), while direct assignment bypasses this system and can lead to test pollution where mocked methods affect subsequent tests.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseLocalVCJenkinsIntegrationTest.java
📚 Learning: 2025-08-14T21:24:50.201Z
Learnt from: SamuelRoettgermann
Repo: ls1intum/Artemis PR: 11111
File: src/main/java/de/tum/cit/aet/artemis/exam/service/ExamRoomService.java:150-152
Timestamp: 2025-08-14T21:24:50.201Z
Learning: In the ExamRoomService.parseAndStoreExamRoomDataFromZipFile method in src/main/java/de/tum/cit/aet/artemis/exam/service/ExamRoomService.java, IllegalArgumentException cannot be thrown in the try block, so only IOException needs to be caught when parsing ZIP files containing exam room JSON data.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseLocalVCJenkinsIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java
📚 Learning: 2025-06-10T12:26:42.449Z
Learnt from: Wallenstein61
Repo: ls1intum/Artemis PR: 10989
File: src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportFromFileService.java:108-117
Timestamp: 2025-06-10T12:26:42.449Z
Learning: In ProgrammingExerciseImportFromFileService, the current directory traversal logic for sharing imports (walking importExerciseDir before extraction) is working correctly in practice and should not be changed to more complex solutions, even if there are theoretical issues with the approach.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseLocalVCJenkinsIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java
📚 Learning: 2025-08-30T22:03:22.332Z
Learnt from: SamuelRoettgermann
Repo: ls1intum/Artemis PR: 11111
File: src/main/java/de/tum/cit/aet/artemis/exam/web/admin/AdminExamResource.java:84-99
Timestamp: 2025-08-30T22:03:22.332Z
Learning: In ExamRoomService.parseAndStoreExamRoomDataFromZipFile, IOException is already properly caught and converted to BAD_REQUEST (400) response, which includes ZipException since it extends IOException. The error handling is appropriate for client-facing errors.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseLocalVCJenkinsIntegrationTest.java
📚 Learning: 2024-10-31T20:40:39.930Z
Learnt from: magaupp
Repo: ls1intum/Artemis PR: 9626
File: src/main/resources/templates/c_sharp/exercise/BubbleSort.cs:9-12
Timestamp: 2024-10-31T20:40:39.930Z
Learning: In the Artemis project, files under the `exercise` directory are incomplete exercises intended to be completed by the student. TODO comments in these files are intentional and should not be implemented.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java
📚 Learning: 2024-10-31T20:40:30.235Z
Learnt from: magaupp
Repo: ls1intum/Artemis PR: 9626
File: src/main/resources/templates/c_sharp/exercise/BubbleSort.cs:3-4
Timestamp: 2024-10-31T20:40:30.235Z
Learning: In this project, files under the `exercise` directory are incomplete exercises to be completed by students. Therefore, review comments suggesting implementations in these files may not be necessary.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java
📚 Learning: 2025-09-25T11:25:54.261Z
Learnt from: Elfari1028
Repo: ls1intum/Artemis PR: 11351
File: src/main/java/de/tum/cit/aet/artemis/versioning/dto/QuizExerciseSnapshotDTO.java:52-53
Timestamp: 2025-09-25T11:25:54.261Z
Learning: In the Artemis versioning system, quiz questions processed by QuizExerciseSnapshotDTO should always have valid IDs (non-null getId()) since versioning works with persisted entities, making null filtering unnecessary according to Elfari1028.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java
📚 Learning: 2025-08-08T08:56:49.931Z
Learnt from: tobias-lippert
Repo: ls1intum/Artemis PR: 11248
File: src/main/java/de/tum/cit/aet/artemis/programming/web/repository/RepositoryResource.java:288-291
Timestamp: 2025-08-08T08:56:49.931Z
Learning: In the Artemis codebase, the LocalCI profile is only active when LocalVC is active (LocalCI ⇒ LocalVC). Therefore, in RepositoryResource.commitChanges, guarding the LocalVCServletService invocation with profileService.isLocalCIActive() is correct and guarantees the bean’s presence.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java
📚 Learning: 2025-08-11T13:21:25.531Z
Learnt from: ekayandan
Repo: ls1intum/Artemis PR: 11027
File: src/main/java/de/tum/cit/aet/artemis/programming/service/GitRepositoryExportService.java:184-186
Timestamp: 2025-08-11T13:21:25.531Z
Learning: The `getBareRepository` method in `GitService` returns a domain `Repository` object (from `de.tum.cit.aet.artemis.programming.domain.Repository`), not a JGit `Repository` (from `org.eclipse.jgit.lib.Repository`).

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java
📚 Learning: 2025-06-15T13:49:43.096Z
Learnt from: ahmetsenturk
Repo: ls1intum/Artemis PR: 10916
File: src/main/java/de/tum/cit/aet/artemis/atlas/web/LearnerProfileResource.java:36-41
Timestamp: 2025-06-15T13:49:43.096Z
Learning: In the Artemis codebase, the development team has decided to allow direct injection of repositories into REST resources rather than always delegating to service layers. This architectural decision was communicated and decided with peer developers.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java
📚 Learning: 2024-06-10T19:44:09.116Z
Learnt from: valentin-boehm
Repo: ls1intum/Artemis PR: 7384
File: src/test/java/de/tum/in/www1/artemis/exam/StudentExamIntegrationTest.java:2804-2810
Timestamp: 2024-06-10T19:44:09.116Z
Learning: The `postWithoutLocation` method used in the `testAbandonStudentExam` test case already checks the response status, ensuring that the abandonment of the exam is accepted.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java
📚 Learning: 2025-01-25T17:22:31.410Z
Learnt from: magaupp
Repo: ls1intum/Artemis PR: 10202
File: src/main/resources/templates/ruby/test/test/test_structural.rb:5-18
Timestamp: 2025-01-25T17:22:31.410Z
Learning: When reviewing exercise templates, avoid adding tests that would pass immediately with the starter code. Tests should verify the actual implementation that students need to complete, not just the presence of classes and methods defined in the template.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java
📚 Learning: 2025-01-09T06:36:04.666Z
Learnt from: chrisknedl
Repo: ls1intum/Artemis PR: 10118
File: src/main/resources/templates/aeolus/java/plain_maven_blackbox.sh:33-41
Timestamp: 2025-01-09T06:36:04.666Z
Learning: In the Artemis build agent's Aeolus templates for Java/Maven blackbox tests, the existence of tool directories and config files is manually verified before script execution, making runtime existence checks redundant.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java
🧬 Code graph analysis (3)
src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingSubmissionIntegrationTest.java (1)
src/test/java/de/tum/cit/aet/artemis/programming/util/RepositoryExportTestUtil.java (1)
  • RepositoryExportTestUtil (38-331)
src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseParticipationIntegrationTest.java (3)
src/main/java/de/tum/cit/aet/artemis/programming/domain/VcsRepositoryUri.java (1)
  • VcsRepositoryUri (11-180)
src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCRepositoryUri.java (1)
  • LocalVCRepositoryUri (17-318)
src/test/java/de/tum/cit/aet/artemis/programming/util/RepositoryExportTestUtil.java (1)
  • RepositoryExportTestUtil (38-331)
src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java (1)
src/test/java/de/tum/cit/aet/artemis/programming/util/RepositoryExportTestUtil.java (1)
  • RepositoryExportTestUtil (38-331)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: Build and Push Docker Image / Build linux/arm64 Docker Image for ls1intum/artemis
  • GitHub Check: Build and Push Docker Image / Build linux/amd64 Docker Image for ls1intum/artemis
  • GitHub Check: Build .war artifact
  • GitHub Check: client-style
  • GitHub Check: server-tests
  • GitHub Check: server-style
  • GitHub Check: client-tests
  • GitHub Check: Analyse
  • GitHub Check: bean-instantiation-check
🔇 Additional comments (2)
src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseParticipationIntegrationTest.java (1)

98-100: Consider localizing the spy bean or adding explicit reset.

The class-level @MockitoSpyBean means every test method interacts with the spy, even though the comment indicates it should only be used for "simulating non-feasible failure scenarios." This increases the risk of test pollution if stubs from one test (e.g., line 1052's doThrow) are not properly cleaned up before the next test runs.

Consider either:

  1. Moving the spy bean to the specific test method that needs it (if framework supports it), or
  2. Adding a @BeforeEach or @AfterEach method to reset the spy using Mockito.reset(gitServiceSpy).
⛔ Skipped due to learnings
Learnt from: Wallenstein61
Repo: ls1intum/Artemis PR: 10989
File: src/test/java/de/tum/cit/aet/artemis/programming/AbstractProgrammingIntegrationLocalCILocalVCTest.java:3-9
Timestamp: 2025-08-10T18:33:22.476Z
Learning: In the Artemis test framework, `MockitoBean` from `org.springframework.test.context.bean.override.mockito` is the standard annotation used for mocking beans in test classes, not `MockBean`. This is used consistently across test base classes like `AbstractProgrammingIntegrationLocalCILocalVCTest`, `AbstractSpringIntegrationIndependentTest`, and `AbstractSpringIntegrationLocalVCSamlTest`. The project also uses `MockitoSpyBean` from the same package.
Learnt from: SamuelRoettgermann
Repo: ls1intum/Artemis PR: 11378
File: src/main/java/de/tum/cit/aet/artemis/exam/service/ExamRoomDistributionService.java:13-16
Timestamp: 2025-09-15T11:18:26.439Z
Learning: In ExamRoomDistributionService.distributeRegisteredStudents method in src/main/java/de/tum/cit/aet/artemis/exam/service/ExamRoomDistributionService.java, the team has decided not to use Transactional annotation despite multiple repository operations, based on human reviewer consultation.
Learnt from: bassner
Repo: ls1intum/Artemis PR: 10705
File: src/test/java/de/tum/cit/aet/artemis/lecture/service/SlideUnhideServiceTest.java:54-57
Timestamp: 2025-07-07T11:43:11.736Z
Learning: In the Artemis test framework, the AbstractArtemisIntegrationTest base class provides common MockitoSpyBean fields like instanceMessageSendService as protected fields, making them available to all test subclasses through inheritance.
Learnt from: magaupp
Repo: ls1intum/Artemis PR: 9751
File: src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseFactory.java:143-148
Timestamp: 2024-11-26T20:43:17.588Z
Learning: In `src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseFactory.java`, the test package name assigned in `populateUnreleasedProgrammingExercise` does not need to adhere to naming conventions.
Learnt from: ekayandan
Repo: ls1intum/Artemis PR: 11027
File: src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java:1839-1839
Timestamp: 2025-08-27T09:46:36.480Z
Learning: In the Artemis codebase, when stubbing GitService.getBareRepository() in tests, it's valid to return a JGit Repository (org.eclipse.jgit.lib.Repository) even though the method signature returns the domain Repository (de.tum.cit.aet.artemis.programming.domain.Repository), because the domain Repository extends JGit's FileRepository, making them polymorphically compatible.
Learnt from: bensofficial
Repo: ls1intum/Artemis PR: 9608
File: src/main/java/de/tum/cit/aet/artemis/programming/service/gitlabci/GitLabCIService.java:202-203
Timestamp: 2024-10-27T11:50:42.955Z
Learning: In `src/main/java/de/tum/cit/aet/artemis/programming/service/gitlabci/GitLabCIService.java`, exception messages are used internally, and minor inconsistencies are acceptable due to performance considerations.
Learnt from: magaupp
Repo: ls1intum/Artemis PR: 9626
File: src/main/resources/templates/c_sharp/exercise/BubbleSort.cs:3-4
Timestamp: 2024-10-31T20:40:30.235Z
Learning: In this project, files under the `exercise` directory are incomplete exercises to be completed by students. Therefore, review comments suggesting implementations in these files may not be necessary.
Learnt from: magaupp
Repo: ls1intum/Artemis PR: 8802
File: src/main/resources/templates/rust/exercise/src/context.rs:1-1
Timestamp: 2024-10-08T15:35:42.972Z
Learning: Code inside the `exercise` directories in the Artemis platform is provided to students and is intended for them to implement. TODO comments in these directories are meant to guide students and should not be addressed in the PR.
Learnt from: magaupp
Repo: ls1intum/Artemis PR: 8802
File: src/main/resources/templates/rust/exercise/src/context.rs:1-1
Timestamp: 2024-08-05T00:11:50.650Z
Learning: Code inside the `exercise` directories in the Artemis platform is provided to students and is intended for them to implement. TODO comments in these directories are meant to guide students and should not be addressed in the PR.
Learnt from: Michael-Breu-UIbk
Repo: ls1intum/Artemis PR: 10989
File: src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.with-sharing.spec.ts:167-169
Timestamp: 2025-09-01T13:47:02.624Z
Learning: In Jest tests, prefer using jest.spyOn() over direct method assignment for mocking service methods, as spies are automatically restored by Jest's cleanup mechanisms (jest.restoreAllMocks()), while direct assignment bypasses this system and can lead to test pollution where mocked methods affect subsequent tests.
src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseLocalVCJenkinsIntegrationTest.java (1)

525-528: testExportProgrammingExerciseInstructorMaterial_failToCreateTempDir: modernized failure injection and signature update

Switching from static Files mocking to stubbing zipFileService.createTemporaryZipFile(any(Path.class), anyList(), anyLong()) is cleaner and closer to the production abstraction, and the updated call

programmingExerciseTestService.exportProgrammingExerciseInstructorMaterial(HttpStatus.INTERNAL_SERVER_ERROR, true, false, false);

correctly reflects the new helper signature (status + three boolean flags).

No issues here; this should robustly simulate the “temp ZIP creation fails” scenario.

@github-actions
Copy link

End-to-End (E2E) Test Results Summary

TestsPassed ☑️Skipped ⚠️Failed ❌️Time ⏱
End-to-End (E2E) Test Report205 ran199 passed3 skipped3 failed1h 19m 31s 674ms
TestResultTime ⏱
End-to-End (E2E) Test Report
e2e/exercise/quiz-exercise/QuizExerciseDropLocation.spec.ts
ts.Quiz Exercise Drop Location Spec › DnD Quiz drop locations › Checks drop locations❌ failure2m 3s 289ms
e2e/exam/test-exam/TestExamStudentExams.spec.ts
ts.Test Exam - student exams › Check exam participants and their submissions › Search for a student in exams❌ failure6m 10s 484ms
e2e/exercise/programming/ProgrammingExerciseStaticCodeAnalysis.spec.ts
ts.Static code analysis tests › Configures SCA grading and makes a successful submission with SCA errors❌ failure2m 33s 304ms

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/test/java/de/tum/cit/aet/artemis/programming/util/RepositoryExportTestUtil.java (1)

356-363: Align student bare-repo slug with LocalVC slug conventions

deleteStudentBareRepo currently builds the slug as exercise.getShortName() + "-" + username. Other LocalVC helpers (e.g. LocalVCRepositoryUri and LocalVCLocalCITestService) appear to follow a project-key–based slug scheme. If the actual student repo slug is derived differently (e.g. from projectKey), this method may silently delete the wrong path or nothing at all.

Consider delegating slug construction to the same logic used when creating the student repo (e.g. via LocalVCLocalCITestService.getRepositorySlug(projectKey, username) or by mirroring its pattern), so cleanup can’t drift from creation logic.

src/test/java/de/tum/cit/aet/artemis/athena/AthenaResourceIntegrationTest.java (1)

448-477: Optional: reuse RepositoryExportTestUtil for seeding README.md content

The manual steps to remove test.txt, write README.md, commit, and push are correct but could be slightly simplified by reusing the new helper methods (e.g. writeFilesAndPush) if you want to reduce duplication across tests in future:

-        Path readme = sourceRepo.workingCopyGitRepoFile.toPath().resolve("README.md");
-        FileUtils.writeStringToFile(readme.toFile(), "Initial commit", java.nio.charset.StandardCharsets.UTF_8);
-        sourceRepo.workingCopyGitRepo.add().addFilepattern(".").call();
-        GitService.commit(sourceRepo.workingCopyGitRepo).setMessage("Initial commit").call();
-        sourceRepo.workingCopyGitRepo.push().setRemote("origin").call();
+        RepositoryExportTestUtil.writeFilesAndPush(sourceRepo, Map.of("README.md", "Initial commit"), "Initial commit");

Not required for correctness, but may help keep future tests DRY.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 40ce916 and 906ed8e.

📒 Files selected for processing (2)
  • src/test/java/de/tum/cit/aet/artemis/athena/AthenaResourceIntegrationTest.java (8 hunks)
  • src/test/java/de/tum/cit/aet/artemis/programming/util/RepositoryExportTestUtil.java (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
src/test/java/**/*.java

⚙️ CodeRabbit configuration file

test_naming: descriptive; test_size: small_specific; fixed_data: true; junit5_features: true; assert_use: assertThat; assert_specificity: true; archunit_use: enforce_package_rules; db_query_count_tests: track_performance; util_service_factory_pattern: true; avoid_db_access: true; mock_strategy: static_mocks; context_restart_minimize: true

Files:

  • src/test/java/de/tum/cit/aet/artemis/athena/AthenaResourceIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/util/RepositoryExportTestUtil.java
🧠 Learnings (21)
📓 Common learnings
Learnt from: magaupp
Repo: ls1intum/Artemis PR: 8802
File: src/main/resources/templates/rust/exercise/src/context.rs:1-1
Timestamp: 2024-10-08T15:35:42.972Z
Learning: Code inside the `exercise` directories in the Artemis platform is provided to students and is intended for them to implement. TODO comments in these directories are meant to guide students and should not be addressed in the PR.
Learnt from: magaupp
Repo: ls1intum/Artemis PR: 8802
File: src/main/resources/templates/rust/exercise/src/context.rs:1-1
Timestamp: 2024-08-05T00:11:50.650Z
Learning: Code inside the `exercise` directories in the Artemis platform is provided to students and is intended for them to implement. TODO comments in these directories are meant to guide students and should not be addressed in the PR.
Learnt from: ekayandan
Repo: ls1intum/Artemis PR: 11027
File: src/main/java/de/tum/cit/aet/artemis/programming/service/GitService.java:1455-1465
Timestamp: 2025-08-11T12:57:51.535Z
Learning: In the Artemis project, when ekayandan responds with "same as above" to a code review suggestion, it means they want to defer the suggested change to a follow-up PR to keep the current PR scope minimal and focused on the core functionality being implemented.
Learnt from: Wallenstein61
Repo: ls1intum/Artemis PR: 10989
File: src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportFromFileService.java:108-117
Timestamp: 2025-06-10T12:26:42.449Z
Learning: In ProgrammingExerciseImportFromFileService, the current directory traversal logic for sharing imports (walking importExerciseDir before extraction) is working correctly in practice and should not be changed to more complex solutions, even if there are theoretical issues with the approach.
Learnt from: magaupp
Repo: ls1intum/Artemis PR: 9751
File: src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseFactory.java:143-148
Timestamp: 2024-11-26T20:43:17.588Z
Learning: In `src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseFactory.java`, the test package name assigned in `populateUnreleasedProgrammingExercise` does not need to adhere to naming conventions.
Learnt from: chrisknedl
Repo: ls1intum/Artemis PR: 10118
File: src/main/resources/templates/aeolus/java/plain_maven_blackbox.sh:33-41
Timestamp: 2025-01-09T06:36:04.666Z
Learning: In the Artemis build agent's Aeolus templates for Java/Maven blackbox tests, the existence of tool directories and config files is manually verified before script execution, making runtime existence checks redundant.
📚 Learning: 2024-11-26T20:43:17.588Z
Learnt from: magaupp
Repo: ls1intum/Artemis PR: 9751
File: src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseFactory.java:143-148
Timestamp: 2024-11-26T20:43:17.588Z
Learning: In `src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseFactory.java`, the test package name assigned in `populateUnreleasedProgrammingExercise` does not need to adhere to naming conventions.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/athena/AthenaResourceIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/util/RepositoryExportTestUtil.java
📚 Learning: 2025-08-11T13:22:05.140Z
Learnt from: Wallenstein61
Repo: ls1intum/Artemis PR: 10989
File: src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceImportTest.java:128-131
Timestamp: 2025-08-11T13:22:05.140Z
Learning: In the ExerciseSharingResourceImportTest class in Artemis, mockServer.verify() is not needed in the tearDown method as the test design doesn't require strict verification of all mocked HTTP expectations. The mockServer field is managed differently in this test class.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/athena/AthenaResourceIntegrationTest.java
📚 Learning: 2024-10-08T15:35:42.972Z
Learnt from: magaupp
Repo: ls1intum/Artemis PR: 8802
File: src/main/resources/templates/rust/exercise/src/context.rs:1-1
Timestamp: 2024-10-08T15:35:42.972Z
Learning: Code inside the `exercise` directories in the Artemis platform is provided to students and is intended for them to implement. TODO comments in these directories are meant to guide students and should not be addressed in the PR.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/athena/AthenaResourceIntegrationTest.java
📚 Learning: 2024-06-10T19:44:09.116Z
Learnt from: valentin-boehm
Repo: ls1intum/Artemis PR: 7384
File: src/main/java/de/tum/in/www1/artemis/service/exam/StudentExamService.java:295-303
Timestamp: 2024-06-10T19:44:09.116Z
Learning: The `abandonStudentExam` method in `StudentExamService` should not include a null check for the `studentExam` parameter as per the project's coding practices. It is expected that the `studentExam` will never be null at this point in the code, and a `NullPointerException` would indicate a significant issue elsewhere in the codebase.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/athena/AthenaResourceIntegrationTest.java
📚 Learning: 2025-06-15T04:13:22.541Z
Learnt from: SamuelRoettgermann
Repo: ls1intum/Artemis PR: 10921
File: src/test/java/de/tum/cit/aet/artemis/exam/ExamIntegrationTest.java:1334-1339
Timestamp: 2025-06-15T04:13:22.541Z
Learning: In Artemis ExamIntegrationTest, time difference assertions use ChronoUnit.MILLIS.between().isLessThan(1) without Math.abs() because the server only stores and retrieves timestamp values without calling now(), making differences predictable and consistent due to serialization/storage precision rather than timing variations.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/athena/AthenaResourceIntegrationTest.java
📚 Learning: 2025-08-26T13:23:05.331Z
Learnt from: tobias-lippert
Repo: ls1intum/Artemis PR: 11318
File: src/main/java/de/tum/cit/aet/artemis/exercise/web/ParticipationResource.java:549-552
Timestamp: 2025-08-26T13:23:05.331Z
Learning: The method findGradeScoresForAllExercisesForCourseAndStudent in StudentParticipationRepository handles both individual and team exercises by combining results from separate queries for individual grades, individual quiz grades, and team grades.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/athena/AthenaResourceIntegrationTest.java
📚 Learning: 2025-08-08T08:50:28.791Z
Learnt from: tobias-lippert
Repo: ls1intum/Artemis PR: 11248
File: src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseParticipationService.java:401-402
Timestamp: 2025-08-08T08:50:28.791Z
Learning: In src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseParticipationService.java, method findStudentParticipationWithLatestSubmissionResultAndFeedbacksElseThrow(long), using List.of() for latestSubmission.setResults(...) is acceptable because the results list is not mutated afterward and is only returned to the client; no follow-up code appends to it.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/athena/AthenaResourceIntegrationTest.java
📚 Learning: 2025-02-11T12:05:49.151Z
Learnt from: janthoXO
Repo: ls1intum/Artemis PR: 9406
File: src/main/java/de/tum/cit/aet/artemis/programming/web/ProgrammingExerciseParticipationResource.java:209-209
Timestamp: 2025-02-11T12:05:49.151Z
Learning: In ProgrammingExerciseParticipationResource, exam-related authorization checks and sensitive information filtering for results and feedbacks are handled by resultService.filterSensitiveInformationIfNecessary().

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/athena/AthenaResourceIntegrationTest.java
📚 Learning: 2025-09-15T11:18:26.439Z
Learnt from: SamuelRoettgermann
Repo: ls1intum/Artemis PR: 11378
File: src/main/java/de/tum/cit/aet/artemis/exam/service/ExamRoomDistributionService.java:13-16
Timestamp: 2025-09-15T11:18:26.439Z
Learning: In ExamRoomDistributionService.distributeRegisteredStudents method in src/main/java/de/tum/cit/aet/artemis/exam/service/ExamRoomDistributionService.java, the team has decided not to use Transactional annotation despite multiple repository operations, based on human reviewer consultation.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/athena/AthenaResourceIntegrationTest.java
📚 Learning: 2025-10-22T21:31:54.240Z
Learnt from: Elfari1028
Repo: ls1intum/Artemis PR: 11491
File: src/main/java/de/tum/cit/aet/artemis/exercise/web/ExerciseResource.java:376-378
Timestamp: 2025-10-22T21:31:54.240Z
Learning: In Artemis, ExerciseVersionService.createExerciseVersion(...) (src/main/java/de/tum/cit/aet/artemis/exercise/service/ExerciseVersionService.java) eagerly re-fetches the target exercise (via type-specific findForVersioningById) before building the ExerciseSnapshotDTO. Controllers (e.g., ExerciseResource.toggleSecondCorrectionEnabled) do not need to reload the exercise before invoking createExerciseVersion.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/athena/AthenaResourceIntegrationTest.java
📚 Learning: 2025-06-17T12:31:09.178Z
Learnt from: jfr2102
Repo: ls1intum/Artemis PR: 10983
File: src/main/java/de/tum/cit/aet/artemis/exercise/repository/StudentParticipationRepository.java:110-126
Timestamp: 2025-06-17T12:31:09.178Z
Learning: The query `findByExamIdWithEagerLatestLegalSubmissionsRatedResultAndIgnoreTestRunParticipation` in StudentParticipationRepository fetches all rated results (not just the latest) because the second correction round feature requires access to multiple assessment results per submission for proper correction round management.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/athena/AthenaResourceIntegrationTest.java
📚 Learning: 2024-10-08T15:35:48.767Z
Learnt from: jakubriegel
Repo: ls1intum/Artemis PR: 8050
File: src/test/java/de/tum/in/www1/artemis/plagiarism/PlagiarismUtilService.java:125-136
Timestamp: 2024-10-08T15:35:48.767Z
Learning: The `createTeamTextExerciseAndSimilarSubmissions` method in `PlagiarismUtilService.java` uses fixed inputs as it is designed to be a test helper method for simplifying the setup of team exercises and submissions in tests.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/athena/AthenaResourceIntegrationTest.java
📚 Learning: 2024-10-08T15:35:48.768Z
Learnt from: SimonEntholzer
Repo: ls1intum/Artemis PR: 8929
File: src/main/java/de/tum/in/www1/artemis/repository/ParticipationVCSAccessTokenRepository.java:27-29
Timestamp: 2024-10-08T15:35:48.768Z
Learning: The method naming convention `deleteByParticipation_id` in `ParticipationVCSAccessTokenRepository.java` is necessary due to specific framework or library constraints in the Artemis project.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/athena/AthenaResourceIntegrationTest.java
📚 Learning: 2025-08-19T09:13:51.235Z
Learnt from: ekayandan
Repo: ls1intum/Artemis PR: 11027
File: src/main/java/de/tum/cit/aet/artemis/programming/service/GitArchiveHelper.java:0-0
Timestamp: 2025-08-19T09:13:51.235Z
Learning: The domain Repository class (de.tum.cit.aet.artemis.programming.domain.Repository) extends JGit's FileRepository, making it polymorphically compatible with JGit APIs while providing additional Artemis-specific methods like getParticipation() and getLocalPath().

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/athena/AthenaResourceIntegrationTest.java
  • src/test/java/de/tum/cit/aet/artemis/programming/util/RepositoryExportTestUtil.java
📚 Learning: 2025-08-27T09:46:36.480Z
Learnt from: ekayandan
Repo: ls1intum/Artemis PR: 11027
File: src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java:1839-1839
Timestamp: 2025-08-27T09:46:36.480Z
Learning: In the Artemis codebase, when stubbing GitService.getBareRepository() in tests, it's valid to return a JGit Repository (org.eclipse.jgit.lib.Repository) even though the method signature returns the domain Repository (de.tum.cit.aet.artemis.programming.domain.Repository), because the domain Repository extends JGit's FileRepository, making them polymorphically compatible.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/athena/AthenaResourceIntegrationTest.java
📚 Learning: 2024-06-10T19:44:09.116Z
Learnt from: valentin-boehm
Repo: ls1intum/Artemis PR: 7384
File: src/test/java/de/tum/in/www1/artemis/exam/StudentExamIntegrationTest.java:988-993
Timestamp: 2024-06-10T19:44:09.116Z
Learning: The `testSubmitStudentExam_differentUser` method does not require additional checks to verify the state of `studentExam1` after receiving a `HttpStatus.FORBIDDEN` because the control flow in the `StudentExamResource` is straightforward and ensures no state change occurs.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/athena/AthenaResourceIntegrationTest.java
📚 Learning: 2024-06-10T19:44:09.116Z
Learnt from: valentin-boehm
Repo: ls1intum/Artemis PR: 7384
File: src/test/java/de/tum/in/www1/artemis/exam/StudentExamIntegrationTest.java:2836-2846
Timestamp: 2024-06-10T19:44:09.116Z
Learning: The `testAbandonStudentExamNotInTime` method does not require additional checks to verify the state of `studentExam1` after receiving a `HttpStatus.FORBIDDEN` because the control flow in the `StudentExamResource` is straightforward and ensures no state change occurs.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/athena/AthenaResourceIntegrationTest.java
📚 Learning: 2024-06-10T19:44:09.116Z
Learnt from: valentin-boehm
Repo: ls1intum/Artemis PR: 7384
File: src/test/java/de/tum/in/www1/artemis/exam/StudentExamIntegrationTest.java:975-980
Timestamp: 2024-06-10T19:44:09.116Z
Learning: The `testSubmitStudentExam_notInTime` method does not require additional checks to verify the state of `studentExam1` after receiving a `HttpStatus.FORBIDDEN` because the control flow in the `StudentExamResource` is straightforward and ensures no state change occurs.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/athena/AthenaResourceIntegrationTest.java
📚 Learning: 2025-06-06T14:47:54.300Z
Learnt from: Wallenstein61
Repo: ls1intum/Artemis PR: 10989
File: src/main/java/de/tum/cit/aet/artemis/programming/web/ExerciseSharingResource.java:122-135
Timestamp: 2025-06-06T14:47:54.300Z
Learning: In Artemis sharing platform integration (under "sharing" profile), endpoints in ExerciseSharingResource use checksum-based authentication with shared secrets instead of PreAuthorize annotations. These endpoints (like getProblemStatement, getExerciseDetails, loadShoppingBasket) validate security through sharingInfo.checkChecksum() calls rather than role-based access control, as they serve as API interfaces for external sharing platform communication.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/athena/AthenaResourceIntegrationTest.java
📚 Learning: 2025-06-10T12:26:42.449Z
Learnt from: Wallenstein61
Repo: ls1intum/Artemis PR: 10989
File: src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportFromFileService.java:108-117
Timestamp: 2025-06-10T12:26:42.449Z
Learning: In ProgrammingExerciseImportFromFileService, the current directory traversal logic for sharing imports (walking importExerciseDir before extraction) is working correctly in practice and should not be changed to more complex solutions, even if there are theoretical issues with the approach.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/util/RepositoryExportTestUtil.java
🧬 Code graph analysis (2)
src/test/java/de/tum/cit/aet/artemis/athena/AthenaResourceIntegrationTest.java (2)
src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCRepositoryUri.java (1)
  • LocalVCRepositoryUri (17-318)
src/test/java/de/tum/cit/aet/artemis/programming/util/RepositoryExportTestUtil.java (1)
  • RepositoryExportTestUtil (42-399)
src/test/java/de/tum/cit/aet/artemis/programming/util/RepositoryExportTestUtil.java (1)
src/main/webapp/app/programming/shared/entities/programming-exercise.model.ts (1)
  • ProgrammingExercise (47-96)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: Build and Push Docker Image / Build linux/arm64 Docker Image for ls1intum/artemis
  • GitHub Check: Build and Push Docker Image / Build linux/amd64 Docker Image for ls1intum/artemis
  • GitHub Check: Build .war artifact
  • GitHub Check: client-style
  • GitHub Check: client-tests
  • GitHub Check: server-style
  • GitHub Check: server-tests
  • GitHub Check: Analyse
  • GitHub Check: bean-instantiation-check
🔇 Additional comments (4)
src/test/java/de/tum/cit/aet/artemis/programming/util/RepositoryExportTestUtil.java (1)

44-107: Lifecycle tracking + automatic cleanup look solid

The ThreadLocal-based trackRepository / cleanupTrackedRepositories design is a good fit for JUnit 5 parallel runs and nicely centralizes LocalRepository cleanup, especially with the auto-tracking in the seeding helpers. This directly mitigates the LocalRepository leak risk mentioned in the PR objectives while keeping tests simple.

src/test/java/de/tum/cit/aet/artemis/athena/AthenaResourceIntegrationTest.java (3)

113-120: Good integration of LocalVC helpers with centralized cleanup

Autowiring LocalVCLocalCITestService / participation helpers and introducing @AfterEach cleanupRepositories() that delegates to RepositoryExportTestUtil.cleanupTrackedRepositories() nicely ensures all LocalRepository instances registered via trackRepository (or the seeding helpers) are consistently cleaned up after each test. This addresses the earlier LocalRepository resource-leak concern without complicating individual tests.

Also applies to: 177-180


417-446: Repository export setup now uses tracked LocalRepository and LocalVC TESTS slug

Switching to RepositoryExportTestUtil.trackRepository(new LocalRepository(defaultBranch)) and deriving the tests repo slug via RepositoryType.TESTS.getName() before wiring the LocalVC URI keeps the setup consistent with the new LocalVC-based test utilities, while ensuring the source repo is included in the automatic cleanup. The updated wiring and subsequent copyBareRepositoryWithoutHistory calls look coherent.


448-495: Student repository export test is thorough and aligns with LocalVC utilities

The new testStudentRepositoryExportEndpoint:

  • Creates a real programming participation + submission via ParticipationUtilService.
  • Seeds a LocalVC repo (athena-src) with a single README.md commit and pushes it.
  • Clones that into a LocalVC student bare repo via RepositoryExportTestUtil.seedLocalVcBareFrom(...).
  • Persists the correct LocalVC repository URI on the student participation.
  • Calls the internal Athena export endpoint and asserts the exact file map.

Combined with the @AfterEach cleanup that handles both repos via tracking, this gives solid end-to-end coverage of the student-repo export path without leaking resources.

@github-actions
Copy link

End-to-End (E2E) Test Results Summary

TestsPassed ✅SkippedFailedTime ⏱
End-to-End (E2E) Test Report1 ran1 passed0 skipped0 failed1s 763ms
TestResultTime ⏱
No test annotations available

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/test/java/de/tum/cit/aet/artemis/exercise/participation/util/ParticipationUtilService.java (1)

354-359: Based on the verification, Linux filesystems are case-sensitive and Git is case-sensitive. The review comment correctly identifies a real issue: Linux treats files/directories with different casing as completely different files/directories.

From the codebase analysis:

  • LocalVCRepositoryUri stores and uses the projectKey directly in filesystem path construction (line 50)
  • Test files consistently apply .toUpperCase() on projectKey (6+ locations)
  • The ParticipationUtilService line 354 calls .toLowerCase(), diverging from this pattern

This will create separate repository directories on Linux, breaking consistency and causing duplicate/orphaned repositories.

Apply uppercase normalization to match codebase conventions.

Using .toUpperCase() ensures consistency with test utilities and other LocalVC call sites, preventing case-mismatched repository paths.

-        var localVcRepoUri = new LocalVCRepositoryUri(localVCBaseUri, exercise.getProjectKey().toLowerCase(), repoName);
+        var localVcRepoUri = new LocalVCRepositoryUri(localVCBaseUri, exercise.getProjectKey().toUpperCase(), repoName);
src/test/java/de/tum/cit/aet/artemis/programming/RepositoryIntegrationTest.java (2)

827-859: Potential resource leak: temp directory created outside try block.

The temp directory is created at line 828 before the try-with-resources block begins at line 829. If an exception occurs between these lines (e.g., from new LocalVCRepositoryUri()), the directory won't be cleaned up by the finally block.

Consider moving the directory creation inside the try block or wrapping the entire sequence:

-    Path remoteClonePath = Files.createTempDirectory("repositoryintegration-remote-clone");
-    try (Repository remoteRepository = gitService.getOrCheckoutRepositoryWithLocalPath(repositoryUri, remoteClonePath, true, true);
-            Git remoteGit = Git.wrap(remoteRepository)) {
+    Path remoteClonePath = null;
+    try {
+        remoteClonePath = Files.createTempDirectory("repositoryintegration-remote-clone");
+        try (Repository remoteRepository = gitService.getOrCheckoutRepositoryWithLocalPath(repositoryUri, remoteClonePath, true, true);
+                Git remoteGit = Git.wrap(remoteRepository)) {
+            // ... test code ...
+        }
+    }
+    finally {
+        if (remoteClonePath != null) {
+            FileUtils.deleteQuietly(remoteClonePath.toFile());
+        }
     }
-    finally {
-        FileUtils.deleteQuietly(remoteClonePath.toFile());
-    }

866-922: Potential resource leak: temp directory created outside try block.

Same issue as in testPullChanges - the temp directory is created at line 867 before the try-with-resources block at line 868.

Apply the same fix pattern as suggested for testPullChanges to ensure the temp directory is always cleaned up.

♻️ Duplicate comments (4)
src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseIntegrationTestService.java (1)

2227-2227: Remove overly broad Exception from throws clause.

Declaring a bare Exception alongside specific checked exceptions defeats the purpose of Java's checked exception system and is considered poor practice.

Either remove Exception entirely if the three specific exceptions (IOException, GitAPIException, URISyntaxException) cover all cases:

-private void setupExerciseForExport() throws IOException, GitAPIException, URISyntaxException, Exception {
+private void setupExerciseForExport() throws IOException, GitAPIException, URISyntaxException {

Or replace it with the specific checked exception type that the method body actually throws (inspect the method implementation to identify what else is thrown beyond the three already declared).

src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingSubmissionIntegrationTest.java (1)

773-779: The past review concern about LocalRepository cleanup is already addressed.

The seedStudentRepositoryForParticipation call on line 774 internally registers the repository for automatic cleanup (per utility class design). The cleanupTrackedRepositories() call in tearDown (line 102) handles all tracked repositories, including those created by this helper. No additional manual tracking is needed.

src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseGitIntegrationTest.java (1)

96-132: Close checkedOut Repository and clean targetPath to prevent resource leaks.

The test creates a checked-out repository and target directory that are never explicitly cleaned:

  1. checkedOut Repository: Returned from getOrCheckoutRepositoryWithTargetPath() (line 115), it holds git file locks on targetPath. Must call closeBeforeDelete() before the test ends.

  2. targetPath directory: Created by the checkout operation but never deleted. Since tempPath is configuration-managed (not JUnit's @tempdir), subdirectories must be cleaned explicitly.

The remoteRepo cleanup via trackRepository() + @AfterEach is already correct and requires no change.

Add a try-finally block to guarantee cleanup of the checked-out repository:

 // Build the LocalVC URI and checkout to a separate target path
 LocalVCRepositoryUri repoUri = new LocalVCRepositoryUri(localVCLocalCITestService.buildLocalVCUri(null, null, projectKey, repoSlug));
 Path targetPath = tempPath.resolve("lcvc-checkout").resolve("student-checkout");
-var checkedOut = gitService.getOrCheckoutRepositoryWithTargetPath(repoUri, targetPath, true, true);
-
-// Verify we can fetch and read last commit hash from the remote
-gitService.fetchAll(checkedOut);
-var lastHash = gitService.getLastCommitHash(repoUri);
-assertThat(lastHash).as("last commit hash should exist on remote").isNotNull().isInstanceOf(ObjectId.class);
-
-// Create a local change, commit and push via GitService
-var localFile = targetPath.resolve("hello.txt");
-Files.createDirectories(localFile.getParent());
-FileUtils.writeStringToFile(localFile.toFile(), "hello world", java.nio.charset.StandardCharsets.UTF_8);
-gitService.stageAllChanges(checkedOut);
-gitService.commitAndPush(checkedOut, "Add hello.txt", true, null);
-
-// Pull and reset operations should not throw
-gitService.pullIgnoreConflicts(checkedOut);
-gitService.resetToOriginHead(checkedOut);
+var checkedOut = gitService.getOrCheckoutRepositoryWithTargetPath(repoUri, targetPath, true, true);
+try {
+    // Verify we can fetch and read last commit hash from the remote
+    gitService.fetchAll(checkedOut);
+    var lastHash = gitService.getLastCommitHash(repoUri);
+    assertThat(lastHash).as("last commit hash should exist on remote").isNotNull().isInstanceOf(ObjectId.class);
+
+    // Create a local change, commit and push via GitService
+    var localFile = targetPath.resolve("hello.txt");
+    Files.createDirectories(localFile.getParent());
+    FileUtils.writeStringToFile(localFile.toFile(), "hello world", java.nio.charset.StandardCharsets.UTF_8);
+    gitService.stageAllChanges(checkedOut);
+    gitService.commitAndPush(checkedOut, "Add hello.txt", true, null);
+
+    // Pull and reset operations should not throw
+    gitService.pullIgnoreConflicts(checkedOut);
+    gitService.resetToOriginHead(checkedOut);
+}
+finally {
+    checkedOut.closeBeforeDelete();
+    RepositoryExportTestUtil.safeDeleteDirectory(targetPath);
+}
src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java (1)

576-616: Track repositories created in setupExerciseForExport to prevent leaks.

The three createAndConfigureLocalRepository(...) calls at lines 611–613 return LocalRepository objects but are not tracked, so they won't be cleaned up by cleanupTrackedRepositories().

-        localVCLocalCITestService.createAndConfigureLocalRepository(projectKey, templateRepositorySlug);
-        localVCLocalCITestService.createAndConfigureLocalRepository(projectKey, solutionRepositorySlug);
-        localVCLocalCITestService.createAndConfigureLocalRepository(projectKey, testsRepositorySlug);
+        RepositoryExportTestUtil.trackRepository(localVCLocalCITestService.createAndConfigureLocalRepository(projectKey, templateRepositorySlug));
+        RepositoryExportTestUtil.trackRepository(localVCLocalCITestService.createAndConfigureLocalRepository(projectKey, solutionRepositorySlug));
+        RepositoryExportTestUtil.trackRepository(localVCLocalCITestService.createAndConfigureLocalRepository(projectKey, testsRepositorySlug));
🧹 Nitpick comments (10)
src/test/java/de/tum/cit/aet/artemis/exam/StudentExamIntegrationTest.java (1)

237-240: Consider adding documentation for the commit hash maps.

These maps track participation commit hashes for verification in programming exercise submission tests. A brief JavaDoc comment would improve readability:

+    // Maps participation ID to commit hashes for verifying programming submissions
+    // initialCommitHashes: first commit after participation setup
+    // updatedCommitHashes: commit after subsequent changes
     private final Map<Long, String> programmingInitialCommitHashes = new HashMap<>();

     private final Map<Long, String> programmingUpdatedCommitHashes = new HashMap<>();
src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseIntegrationTestService.java (1)

431-432: Remove redundant file deletions.

The Files.deleteIfExists() calls on lines 431-432 are redundant because projectFilePath and pomPath reside inside repo1.workingCopyGitRepoFile.toPath() (as seen on lines 404-405). When cleanupTrackedRepositories() runs in tearDown() (line 292), it invokes resetLocalRepo() on all tracked repositories, which deletes entire repository directories—including these files.

Remove the explicit file deletions:

     String modifiedPom = Files.readString(repoRoot.resolve("pom.xml"));
     assertThat(modifiedPom).contains((userPrefix + "student1").toLowerCase());
-    Files.deleteIfExists(projectFilePath);
-    Files.deleteIfExists(pomPath);
 }

The cleanup will still occur automatically in tearDown().

src/test/java/de/tum/cit/aet/artemis/programming/TestRepositoryResourceIntegrationTest.java (1)

260-260: Verify disabled tests are tracked for re-enablement.

Five tests are currently disabled (testCommitChanges, testSaveFilesAndCommit, testPullChanges, testResetToLastCommit, testGetStatus). These cover critical Git operations and represent significant test coverage.

Ensure these tests are tracked in an issue or task list for re-enablement after the LocalVC migration is complete.

Would you like me to help generate updated implementations for these disabled tests, or is there already a tracking issue for this work?

Also applies to: 296-296, 327-327, 362-362, 418-418

src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseGitIntegrationTest.java (1)

117-120: Optional: Consider verifying commit hash value.

The current assertion confirms lastHash is non-null and of type ObjectId, which validates the basic functionality. For stricter verification, you could compare it against the actual HEAD commit from remoteRepo.workingCopyGitRepo.

Example enhancement (not required for this PR):

// Get expected hash from remote
ObjectId expectedHash = remoteRepo.workingCopyGitRepo.getRepository().resolve("HEAD");
assertThat(lastHash).as("last commit hash should match remote HEAD").isEqualTo(expectedHash);
src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseParticipationIntegrationTest.java (5)

112-115: Repository cleanup strategy properly addresses LocalRepository resource leaks

Calling RepositoryExportTestUtil.cleanupTrackedRepositories() in @AfterEach together with consistently wrapping new LocalVC repos in RepositoryExportTestUtil.trackRepository(...) (e.g. in ensureAuxiliaryRepositoryConfigured and ensureLocalVcRepositoryExists) ensures LocalRepository instances and their directories are reset and removed after each test, preventing disk bloat and cross-test interference.

If this cleanup pattern is used in multiple classes, consider a small shared base/helper in the test hierarchy to avoid duplication.

Also applies to: 944-952, 1151-1152


875-895: Seeding all base and auxiliary repositories with real commits is a solid improvement

The setup() in GetCommitHistoryForTemplateSolutionTestOrAuxRepo now wires LocalVC repos via createAndWireBaseRepositories, commits distinct messages to template/solution/tests, and commits to a properly configured auxiliary repo. This removes fragile stubbing and gives the commit-history endpoint realistic data to work with.

You could centralize the repeated “commit message constants + initial commit” pattern into a small helper if more tests need similar seeding, but this is not urgent.

Also applies to: 890-895


981-1012: Commit-details view tests comprehensively cover student/template/solution sources

The nested GetParticipationRepositoryFilesForCommitsDetailsView setup seeds:

  • a student participation repo,
  • the template repo, and
  • the solution repo

with distinct commits and then verifies the files-content-commit-details endpoint for each parameter combination. This eliminates the previous reliance on mocks and accurately tests how the controller interacts with LocalVC.

If more repositories (e.g. auxiliary) are later added to this view, consider extracting a small shared seeding utility to keep setup concise.

Also applies to: 1020-1039


1122-1153: LocalVC commit helpers encapsulate URI handling and repository bootstrapping well

The overloads of commitToRepository(...):

  • Normalize any VcsRepositoryUri to a LocalVCRepositoryUri, throwing a clear IllegalStateException if the URI is missing.
  • Ensure the local bare repo exists (creating and tracking it if needed) via ensureLocalVcRepositoryExists.
  • Delegate to a single implementation that works on LocalVCRepositoryUri.

This keeps LocalVC assumptions localized in one place and makes higher-level tests concise.

If other test classes need the same behavior, consider moving these helpers to RepositoryExportTestUtil or a dedicated LocalVC test helper to avoid duplication.


1154-1172: writeFilesAndPush correctly manages working copies and resources

writeFilesAndPush:

  • clones from the bare repo using a file:// URI into a temp directory under tempPath,
  • writes all requested files, commits with the supplied message via GitService.commit(git), and pushes, and
  • cleans up the working copy in a finally block using RepositoryExportTestUtil.safeDeleteDirectory.

Resource usage (JGit handle + temp directories) is properly bounded; no leaks are apparent.

You might consider reusing any existing writeFilesAndPush(LocalRepository, ...) helper from RepositoryExportTestUtil in the future to avoid slightly similar commit logic in multiple places, but this is optional.

src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java (1)

361-394: Optional: rely solely on centralized cleanup to reduce duplication.

cleanupTrackedRepositories() already resets tracked repos. The per-field reset blocks duplicate the work and add noise.

Option:

  • Keep cleanupTrackedRepositories() and remove the explicit per-field resets (or guard them behind a debug flag).

@github-actions
Copy link

End-to-End (E2E) Test Results Summary

TestsPassed ☑️Skipped ⚠️Failed ❌️Time ⏱
End-to-End (E2E) Test Report205 ran201 passed3 skipped1 failed1h 3m 46s 116ms
TestResultTime ⏱
End-to-End (E2E) Test Report
e2e/exercise/quiz-exercise/QuizExerciseDropLocation.spec.ts
ts.Quiz Exercise Drop Location Spec › DnD Quiz drop locations › Checks drop locations❌ failure2m 3s 145ms

@github-actions
Copy link

End-to-End (E2E) Test Results Summary

TestsPassed ☑️Skipped ⚠️Failed ❌️Time ⏱
End-to-End (E2E) Test Report205 ran201 passed3 skipped1 failed1h 8m 6s 479ms
TestResultTime ⏱
End-to-End (E2E) Test Report
e2e/exercise/quiz-exercise/QuizExerciseDropLocation.spec.ts
ts.Quiz Exercise Drop Location Spec › DnD Quiz drop locations › Checks drop locations❌ failure2m 3s 609ms

@github-actions
Copy link

There hasn't been any activity on this pull request recently. Therefore, this pull request has been automatically marked as stale and will be closed if no further activity occurs within seven days. Thank you for your contributions.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/test/java/de/tum/cit/aet/artemis/programming/TestRepositoryResourceIntegrationTest.java (1)

62-83: TESTS repository seeding, wiring, and initial push correctly prepare LocalVC state

Deriving testsSlug from projectKey and RepositoryType.TESTS, seeding via RepositoryExportTestUtil.seedBareRepository, wiring the TESTS URI onto the ProgrammingExercise, and then committing/pushing ensures the LocalVC‑backed controller will see a fully initialized TESTS repo with both a file and a tracked folder (.keep). This is a good fit with the new repository lifecycle utilities and makes the tests much closer to production behavior.

As a small optional clean‑up, you could avoid manual string path concatenation when building Paths from workingCopyGitRepoFile:

-        Path filePath = Path.of(testRepo.workingCopyGitRepoFile + "/" + currentLocalFileName);
+        Path filePath = testRepo.workingCopyGitRepoFile.toPath().resolve(currentLocalFileName);
@@
-        filePath = Path.of(testRepo.workingCopyGitRepoFile + "/" + currentLocalFolderName);
+        filePath = testRepo.workingCopyGitRepoFile.toPath().resolve(currentLocalFolderName);

(and similarly elsewhere if you touch those lines again).

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1336f73 and e91a7bf.

📒 Files selected for processing (2)
  • src/test/java/de/tum/cit/aet/artemis/programming/TestRepositoryResourceIntegrationTest.java (11 hunks)
  • src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalVCLocalCIIntegrationTest.java (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalVCLocalCIIntegrationTest.java
🧰 Additional context used
📓 Path-based instructions (1)
src/test/java/**/*.java

⚙️ CodeRabbit configuration file

test_naming: descriptive; test_size: small_specific; fixed_data: true; junit5_features: true; assert_use: assertThat; assert_specificity: true; archunit_use: enforce_package_rules; db_query_count_tests: track_performance; util_service_factory_pattern: true; avoid_db_access: true; mock_strategy: static_mocks; context_restart_minimize: true

Files:

  • src/test/java/de/tum/cit/aet/artemis/programming/TestRepositoryResourceIntegrationTest.java
🧠 Learnings (16)
📓 Common learnings
Learnt from: magaupp
Repo: ls1intum/Artemis PR: 8802
File: src/main/resources/templates/rust/exercise/src/context.rs:1-1
Timestamp: 2024-10-08T15:35:42.972Z
Learning: Code inside the `exercise` directories in the Artemis platform is provided to students and is intended for them to implement. TODO comments in these directories are meant to guide students and should not be addressed in the PR.
Learnt from: magaupp
Repo: ls1intum/Artemis PR: 8802
File: src/main/resources/templates/rust/exercise/src/context.rs:1-1
Timestamp: 2024-08-05T00:11:50.650Z
Learning: Code inside the `exercise` directories in the Artemis platform is provided to students and is intended for them to implement. TODO comments in these directories are meant to guide students and should not be addressed in the PR.
Learnt from: Wallenstein61
Repo: ls1intum/Artemis PR: 10989
File: src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportFromFileService.java:108-117
Timestamp: 2025-06-10T12:26:42.449Z
Learning: In ProgrammingExerciseImportFromFileService, the current directory traversal logic for sharing imports (walking importExerciseDir before extraction) is working correctly in practice and should not be changed to more complex solutions, even if there are theoretical issues with the approach.
📚 Learning: 2025-08-27T09:46:36.480Z
Learnt from: ekayandan
Repo: ls1intum/Artemis PR: 11027
File: src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseTestService.java:1839-1839
Timestamp: 2025-08-27T09:46:36.480Z
Learning: In the Artemis codebase, when stubbing GitService.getBareRepository() in tests, it's valid to return a JGit Repository (org.eclipse.jgit.lib.Repository) even though the method signature returns the domain Repository (de.tum.cit.aet.artemis.programming.domain.Repository), because the domain Repository extends JGit's FileRepository, making them polymorphically compatible.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/TestRepositoryResourceIntegrationTest.java
📚 Learning: 2025-08-19T09:13:51.235Z
Learnt from: ekayandan
Repo: ls1intum/Artemis PR: 11027
File: src/main/java/de/tum/cit/aet/artemis/programming/service/GitArchiveHelper.java:0-0
Timestamp: 2025-08-19T09:13:51.235Z
Learning: The domain Repository class (de.tum.cit.aet.artemis.programming.domain.Repository) extends JGit's FileRepository, making it polymorphically compatible with JGit APIs while providing additional Artemis-specific methods like getParticipation() and getLocalPath().

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/TestRepositoryResourceIntegrationTest.java
📚 Learning: 2025-08-10T18:33:22.476Z
Learnt from: Wallenstein61
Repo: ls1intum/Artemis PR: 10989
File: src/test/java/de/tum/cit/aet/artemis/programming/AbstractProgrammingIntegrationLocalCILocalVCTest.java:3-9
Timestamp: 2025-08-10T18:33:22.476Z
Learning: In the Artemis test framework, `MockitoBean` from `org.springframework.test.context.bean.override.mockito` is the standard annotation used for mocking beans in test classes, not `MockBean`. This is used consistently across test base classes like `AbstractProgrammingIntegrationLocalCILocalVCTest`, `AbstractSpringIntegrationIndependentTest`, and `AbstractSpringIntegrationLocalVCSamlTest`. The project also uses `MockitoSpyBean` from the same package.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/TestRepositoryResourceIntegrationTest.java
📚 Learning: 2025-08-11T13:22:05.140Z
Learnt from: Wallenstein61
Repo: ls1intum/Artemis PR: 10989
File: src/test/java/de/tum/cit/aet/artemis/programming/service/sharing/ExerciseSharingResourceImportTest.java:128-131
Timestamp: 2025-08-11T13:22:05.140Z
Learning: In the ExerciseSharingResourceImportTest class in Artemis, mockServer.verify() is not needed in the tearDown method as the test design doesn't require strict verification of all mocked HTTP expectations. The mockServer field is managed differently in this test class.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/TestRepositoryResourceIntegrationTest.java
📚 Learning: 2025-08-08T08:56:49.931Z
Learnt from: tobias-lippert
Repo: ls1intum/Artemis PR: 11248
File: src/main/java/de/tum/cit/aet/artemis/programming/web/repository/RepositoryResource.java:288-291
Timestamp: 2025-08-08T08:56:49.931Z
Learning: In the Artemis codebase, the LocalCI profile is only active when LocalVC is active (LocalCI ⇒ LocalVC). Therefore, in RepositoryResource.commitChanges, guarding the LocalVCServletService invocation with profileService.isLocalCIActive() is correct and guarantees the bean’s presence.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/TestRepositoryResourceIntegrationTest.java
📚 Learning: 2025-06-15T13:49:43.096Z
Learnt from: ahmetsenturk
Repo: ls1intum/Artemis PR: 10916
File: src/main/java/de/tum/cit/aet/artemis/atlas/web/LearnerProfileResource.java:36-41
Timestamp: 2025-06-15T13:49:43.096Z
Learning: In the Artemis codebase, the development team has decided to allow direct injection of repositories into REST resources rather than always delegating to service layers. This architectural decision was communicated and decided with peer developers.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/TestRepositoryResourceIntegrationTest.java
📚 Learning: 2025-07-07T11:43:11.736Z
Learnt from: bassner
Repo: ls1intum/Artemis PR: 10705
File: src/test/java/de/tum/cit/aet/artemis/lecture/service/SlideUnhideServiceTest.java:54-57
Timestamp: 2025-07-07T11:43:11.736Z
Learning: In the Artemis test framework, the AbstractArtemisIntegrationTest base class provides common MockitoSpyBean fields like instanceMessageSendService as protected fields, making them available to all test subclasses through inheritance.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/TestRepositoryResourceIntegrationTest.java
📚 Learning: 2024-10-08T15:35:52.595Z
Learnt from: pzdr7
Repo: ls1intum/Artemis PR: 9124
File: src/test/javascript/spec/component/shared/monaco-editor/monaco-editor-communication-action.integration.spec.ts:112-112
Timestamp: 2024-10-08T15:35:52.595Z
Learning: In the context of tests in the Artemis project, non-null assertions are acceptable and should not be flagged for removal.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/TestRepositoryResourceIntegrationTest.java
📚 Learning: 2025-02-06T17:28:16.450Z
Learnt from: magaupp
Repo: ls1intum/Artemis PR: 10265
File: src/main/resources/templates/dart/test/test/behavior_test.dart:57-67
Timestamp: 2025-02-06T17:28:16.450Z
Learning: In Artemis test files, reflection is intentionally used to bypass potential implementation errors in constructors and setters, allowing tests to produce meaningful results about core functionality even when students haven't implemented everything correctly.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/TestRepositoryResourceIntegrationTest.java
📚 Learning: 2024-11-26T20:43:17.588Z
Learnt from: magaupp
Repo: ls1intum/Artemis PR: 9751
File: src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseFactory.java:143-148
Timestamp: 2024-11-26T20:43:17.588Z
Learning: In `src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseFactory.java`, the test package name assigned in `populateUnreleasedProgrammingExercise` does not need to adhere to naming conventions.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/TestRepositoryResourceIntegrationTest.java
📚 Learning: 2025-06-10T12:26:42.449Z
Learnt from: Wallenstein61
Repo: ls1intum/Artemis PR: 10989
File: src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportFromFileService.java:108-117
Timestamp: 2025-06-10T12:26:42.449Z
Learning: In ProgrammingExerciseImportFromFileService, the current directory traversal logic for sharing imports (walking importExerciseDir before extraction) is working correctly in practice and should not be changed to more complex solutions, even if there are theoretical issues with the approach.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/TestRepositoryResourceIntegrationTest.java
📚 Learning: 2024-06-10T19:44:09.116Z
Learnt from: valentin-boehm
Repo: ls1intum/Artemis PR: 7384
File: src/test/java/de/tum/in/www1/artemis/exam/StudentExamIntegrationTest.java:988-993
Timestamp: 2024-06-10T19:44:09.116Z
Learning: The `testSubmitStudentExam_differentUser` method does not require additional checks to verify the state of `studentExam1` after receiving a `HttpStatus.FORBIDDEN` because the control flow in the `StudentExamResource` is straightforward and ensures no state change occurs.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/TestRepositoryResourceIntegrationTest.java
📚 Learning: 2024-06-10T19:44:09.116Z
Learnt from: valentin-boehm
Repo: ls1intum/Artemis PR: 7384
File: src/test/java/de/tum/in/www1/artemis/exam/StudentExamIntegrationTest.java:975-980
Timestamp: 2024-06-10T19:44:09.116Z
Learning: The `testSubmitStudentExam_notInTime` method does not require additional checks to verify the state of `studentExam1` after receiving a `HttpStatus.FORBIDDEN` because the control flow in the `StudentExamResource` is straightforward and ensures no state change occurs.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/TestRepositoryResourceIntegrationTest.java
📚 Learning: 2024-06-10T19:44:09.116Z
Learnt from: valentin-boehm
Repo: ls1intum/Artemis PR: 7384
File: src/test/java/de/tum/in/www1/artemis/exam/StudentExamIntegrationTest.java:2836-2846
Timestamp: 2024-06-10T19:44:09.116Z
Learning: The `testAbandonStudentExamNotInTime` method does not require additional checks to verify the state of `studentExam1` after receiving a `HttpStatus.FORBIDDEN` because the control flow in the `StudentExamResource` is straightforward and ensures no state change occurs.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/TestRepositoryResourceIntegrationTest.java
📚 Learning: 2024-06-10T19:44:09.116Z
Learnt from: valentin-boehm
Repo: ls1intum/Artemis PR: 7384
File: src/test/java/de/tum/in/www1/artemis/exam/StudentExamIntegrationTest.java:2804-2810
Timestamp: 2024-06-10T19:44:09.116Z
Learning: The `postWithoutLocation` method used in the `testAbandonStudentExam` test case already checks the response status, ensuring that the abandonment of the exam is accepted.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/programming/TestRepositoryResourceIntegrationTest.java
🧬 Code graph analysis (1)
src/test/java/de/tum/cit/aet/artemis/programming/TestRepositoryResourceIntegrationTest.java (1)
src/test/java/de/tum/cit/aet/artemis/programming/util/RepositoryExportTestUtil.java (1)
  • RepositoryExportTestUtil (42-399)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Codacy Static Code Analysis
🔇 Additional comments (12)
src/test/java/de/tum/cit/aet/artemis/programming/TestRepositoryResourceIntegrationTest.java (12)

29-53: LocalVC-based test base and LocalRepository handle align with new test strategy

Extending AbstractProgrammingIntegrationLocalCILocalVCTest and introducing a dedicated LocalRepository testRepo match the PR’s goal of exercising the LocalVC-backed flow end‑to‑end instead of relying on mocks. The imports (RepositoryType, RepositoryExportTestUtil) are consistent with that direction, and the field visibility and naming are clear.


86-89: Centralized LocalRepository cleanup resolves previous resource‑leak concern

Using RepositoryExportTestUtil.cleanupTrackedRepositories() in @AfterEach is the right way to close JGit handles and delete LocalVC directories for all repositories seeded via seedBareRepository, and it removes the need for ad‑hoc resetLocalRepo() calls on testRepo. This directly addresses the earlier concern about lingering filesystem resources between test runs. Based on learnings, this utility already calls resetLocalRepo() on all tracked repositories.


124-133: testCreateFile now validates API‑level creation, not just filesystem state

The additional GET with getParams after the POST ensures that the newly created file is retrievable via the REST API (status asserted via HttpStatus.OK in the helper), not only present on disk. This strengthens the test’s black‑box character and remains small and focused.


135-145: Duplicate‑file handling in testCreateFile_alreadyExists is clearly specified

Creating the file once (expecting OK) and then asserting BAD_REQUEST on the second POST makes the expected behavior for duplicate creation explicit and easy to understand. The test remains small and uses fixed data as per the guidelines.


146-154: testCreateFile_invalidRepository correctly blocks writes into .git

Passing .git/config as the target file and expecting BAD_REQUEST nicely captures the repository‑integrity constraint at the REST level. This is a good, focused negative test and keeps all validation in terms of API behavior.


156-166: testCreateFolder now asserts presence and type in the files listing

After creating the folder via the API, verifying that /files contains "newFolder" with FileType.FOLDER checks both existence and classification, which is more robust than only asserting on the filesystem. This matches the “assert specificity” guideline by asserting exactly the expected map entry.


168-179: testRenameFile validates repository view after rename via /files

The additional filesAfter map check ensures the old file name disappears and the new one appears from the controller’s perspective, not just on disk. This is a tight, behavior‑oriented assertion and keeps the test size small.


183-201: Rename error-path tests cover both existing targets and path traversal

testRenameFile_alreadyExists now prepares the conflicting target file via the same REST API (/file) before asserting BAD_REQUEST on /rename-file, which is a cleaner black‑box setup than manipulating the repo directly. testRenameFile_invalidExistingFile checks that attempts to rename "../malicious" are rejected with BAD_REQUEST, guarding against path traversal on the rename endpoint. Together they give good coverage of rename failure scenarios while staying concise.


203-214: testRenameFolder asserts both absence of old and presence of new folder entry

After renaming the folder, checking that /files no longer contains currentLocalFolderName but does contain newFolderName with FileType.FOLDER ensures the folder rename is reflected properly in the controller’s view, not just at the filesystem level.


218-257: Delete tests now thoroughly cover success, non-existent paths, traversal, and folder deletion

The combination of:

  • testDeleteFile (OK + subsequent GET NOT_FOUND),
  • testDeleteFile_nonExistingPath (BAD_REQUEST for a missing file),
  • testDeleteFile_invalidFile (BAD_REQUEST for "../malicious"), and
  • testDeleteFolder (OK delete of currentLocalFolderName and absence from /files)

gives comprehensive coverage of delete semantics for both files and folders, including error conditions and security‑sensitive paths. All checks are expressed through HTTP status and /files views rather than only direct filesystem assertions, which is ideal for these integration tests.


283-293: testSaveFiles now verifies updated content via the REST API

After PUT with commit=false, constructing params and fetching the file back via /file to assert "updatedFileContent" ensures the controller actually writes the new content, not just that the call succeeds. This keeps the assertion specific and uses AssertJ as required. As per coding guidelines, this is a good example of specific assertThat usage in tests.


445-452: testRepositoryState uses API writes to induce UNCOMMITTED_CHANGES

Creating uncommitted changes via PUT /files?commit=false instead of manual filesystem edits ties the status assertion directly to the REST behavior under test. The assertion against RepositoryStatusDTOType.UNCOMMITTED_CHANGES is precise, and the test remains small and focused on a single state.

@github-actions github-actions bot removed the stale label Nov 25, 2025
@github-actions
Copy link

End-to-End (E2E) Test Results Summary

TestsPassed ☑️Skipped ⚠️Failed ❌️Time ⏱
End-to-End (E2E) Test Report215 ran210 passed3 skipped2 failed1h 8m 59s 472ms
TestResultTime ⏱
End-to-End (E2E) Test Report
e2e/exercise/programming/ProgrammingExerciseParticipation.spec.ts
ts.Programming exercise participation › Programming exercise team participation › Check team participation › Instructor checks the participation❌ failure8s 732ms
e2e/exercise/programming/ProgrammingExerciseStaticCodeAnalysis.spec.ts
ts.Static code analysis tests › Configures SCA grading and makes a successful submission with SCA errors❌ failure2m 42s 185ms

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

assessment Pull requests that affect the corresponding module athena Pull requests that affect the corresponding module core Pull requests that affect the corresponding module exam Pull requests that affect the corresponding module exercise Pull requests that affect the corresponding module programming Pull requests that affect the corresponding module server Pull requests that update Java code. (Added Automatically!) tests

Projects

Status: Ready For Review

Development

Successfully merging this pull request may close these issues.

6 participants